ajax_example_graceful_degradation.inc

You are here

Demonstrations of AJAX with graceful degradation.

Functions

Namesort descending Description
ajax_example_add_more Form with 'add more' and 'remove' buttons.
ajax_example_add_more_add_one Submit handler for the "add-one-more" button.
ajax_example_add_more_callback Callback for both ajax-enabled buttons.
ajax_example_add_more_remove_one Submit handler for the "remove one" button.
ajax_example_add_more_submit Final submit handler.
ajax_example_dependent_dropdown_degrades Dropdown form based on previous choices.
ajax_example_dependent_dropdown_degrades_first_callback Selects just the second dropdown to be returned for re-rendering.
ajax_example_dependent_dropdown_degrades_submit Submit function for ajax_example_dependent_dropdown_degrades().
ajax_example_dynamic_sections Dynamically-enabled form with graceful no-JS degradation.
ajax_example_dynamic_sections_select_callback Callback for the select element.
ajax_example_dynamic_sections_submit Submit function for ajax_example_dynamic_sections().
ajax_example_dynamic_sections_validate Validation function for ajax_example_dynamic_sections().
ajax_example_wizard Wizard form.
ajax_example_wizard_callback Wizard callback function.
ajax_example_wizard_submit Submit function for ajax_example_wizard.

File

ajax_example/ajax_example_graceful_degradation.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Demonstrations of AJAX with graceful degradation.
  5. */
  6. /**
  7. * @defgroup ajax_degradation_example Example: AJAX Graceful Degradation
  8. * @ingroup examples
  9. * @{
  10. * These examples show AJAX with graceful degradation when Javascript is not
  11. * available.
  12. *
  13. * In each of these the key idea is that the form is rebuilt different ways
  14. * depending on form input. In order to accomplish that, the formbuilder
  15. * function is in charge of almost all logic.
  16. */
  17. /**
  18. * Dropdown form based on previous choices.
  19. *
  20. * A form with a dropdown whose options are dependent on a choice made in a
  21. * previous dropdown.
  22. *
  23. * On changing the first dropdown, the options in the second
  24. * are updated. Gracefully degrades if no javascript.
  25. *
  26. * A bit of CSS and javascript is required. The CSS hides the "add more" button
  27. * if javascript is not enabled. The Javascript snippet is really only used
  28. * to enable us to present the form in degraded mode without forcing the user
  29. * to turn off Javascript. Both of these are loaded by using the
  30. * #attached FAPI property, so it is a good example of how to use that.
  31. *
  32. * The extra argument $no_js_use is here only to allow presentation of this
  33. * form as if Javascript were not enabled. ajax_example_menu() provides two
  34. * ways to call this form, one normal ($no_js_use = FALSE) and one simulating
  35. * Javascript disabled ($no_js_use = TRUE).
  36. */
  37. function ajax_example_dependent_dropdown_degrades($form, &$form_state, $no_js_use = FALSE) {
  38. // Get the list of options to populate the first dropdown.
  39. $options_first = _ajax_example_get_first_dropdown_options();
  40. // If we have a value for the first dropdown from $form_state['values'] we use
  41. // this both as the default value for the first dropdown and also as a
  42. // parameter to pass to the function that retrieves the options for the
  43. // second dropdown.
  44. $selected = isset($form_state['values']['dropdown_first']) ? $form_state['values']['dropdown_first'] : key($options_first);
  45. // Attach the CSS and JS we need to show this with and without javascript.
  46. // Without javascript we need an extra "Choose" button, and this is
  47. // hidden when we have javascript enabled.
  48. $form['#attached']['css'] = array(
  49. drupal_get_path('module', 'ajax_example') . '/ajax_example.css',
  50. );
  51. $form['#attached']['js'] = array(
  52. drupal_get_path('module', 'ajax_example') . '/ajax_example.js',
  53. );
  54. $form['dropdown_first_fieldset'] = array(
  55. '#type' => 'fieldset',
  56. );
  57. $form['dropdown_first_fieldset']['dropdown_first'] = array(
  58. '#type' => 'select',
  59. '#title' => 'Instrument Type',
  60. '#options' => $options_first,
  61. '#attributes' => array('class' => array('enabled-for-ajax')),
  62. // The '#ajax' property allows us to bind a callback to the server whenever
  63. // this form element changes. See ajax_example_autocheckboxes and
  64. // ajax_example_dependent_dropdown in ajax_example.module for more details.
  65. '#ajax' => array(
  66. 'callback' => 'ajax_example_dependent_dropdown_degrades_first_callback',
  67. 'wrapper' => 'dropdown-second-replace',
  68. ),
  69. );
  70. // This simply allows us to demonstrate no-javascript use without
  71. // actually turning off javascript in the browser. Removing the #ajax
  72. // element turns off AJAX behaviors on that element and as a result
  73. // ajax.js doesn't get loaded. This is for demonstration purposes only.
  74. if ($no_js_use) {
  75. unset($form['dropdown_first_fieldset']['dropdown_first']['#ajax']);
  76. }
  77. // Since we don't know if the user has js or not, we always need to output
  78. // this element, then hide it with with css if javascript is enabled.
  79. $form['dropdown_first_fieldset']['continue_to_second'] = array(
  80. '#type' => 'submit',
  81. '#value' => t('Choose'),
  82. '#attributes' => array('class' => array('next-button')),
  83. );
  84. $form['dropdown_second_fieldset'] = array(
  85. '#type' => 'fieldset',
  86. );
  87. $form['dropdown_second_fieldset']['dropdown_second'] = array(
  88. '#type' => 'select',
  89. '#title' => $options_first[$selected] . ' ' . t('Instruments'),
  90. '#prefix' => '<div id="dropdown-second-replace">',
  91. '#suffix' => '</div>',
  92. '#attributes' => array('class' => array('enabled-for-ajax')),
  93. // When the form is rebuilt during processing (either AJAX or multistep),
  94. // the $selected variable will now have the new value and so the options
  95. // will change.
  96. '#options' => _ajax_example_get_second_dropdown_options($selected),
  97. );
  98. $form['dropdown_second_fieldset']['submit'] = array(
  99. '#type' => 'submit',
  100. '#value' => t('OK'),
  101. // This class allows attached js file to override the disabled attribute,
  102. // since it's not necessary in ajax-enabled form.
  103. '#attributes' => array('class' => array('enabled-for-ajax')),
  104. );
  105. // Disable dropdown_second if a selection has not been made on dropdown_first.
  106. if (empty($form_state['values']['dropdown_first'])) {
  107. $form['dropdown_second_fieldset']['dropdown_second']['#disabled'] = TRUE;
  108. $form['dropdown_second_fieldset']['dropdown_second']['#description'] = t('You must make your choice on the first dropdown before changing this second one.');
  109. $form['dropdown_second_fieldset']['submit']['#disabled'] = TRUE;
  110. }
  111. return $form;
  112. }
  113. /**
  114. * Submit function for ajax_example_dependent_dropdown_degrades().
  115. */
  116. function ajax_example_dependent_dropdown_degrades_submit($form, &$form_state) {
  117. // Now handle the case of the next, previous, and submit buttons.
  118. // only submit will result in actual submission, all others rebuild.
  119. switch ($form_state['triggering_element']['#value']) {
  120. case t('OK'):
  121. // Submit: We're done.
  122. drupal_set_message(t('Your values have been submitted. dropdown_first=@first, dropdown_second=@second', array('@first' => $form_state['values']['dropdown_first'], '@second' => $form_state['values']['dropdown_second'])));
  123. return;
  124. }
  125. // 'Choose' or anything else will cause rebuild of the form and present
  126. // it again.
  127. $form_state['rebuild'] = TRUE;
  128. }
  129. /**
  130. * Selects just the second dropdown to be returned for re-rendering.
  131. *
  132. * @return array
  133. * Renderable array (the second dropdown).
  134. */
  135. function ajax_example_dependent_dropdown_degrades_first_callback($form, $form_state) {
  136. return $form['dropdown_second_fieldset']['dropdown_second'];
  137. }
  138. /**
  139. * Dynamically-enabled form with graceful no-JS degradation.
  140. *
  141. * Example of a form with portions dynamically enabled or disabled, but
  142. * with graceful degradation in the case of no javascript.
  143. *
  144. * The idea here is that certain parts of the form don't need to be displayed
  145. * unless a given option is selected, but then they should be displayed and
  146. * configured.
  147. *
  148. * The third $no_js_use argument is strictly for demonstrating operation
  149. * without javascript, without making the user/developer turn off javascript.
  150. */
  151. function ajax_example_dynamic_sections($form, &$form_state, $no_js_use = FALSE) {
  152. // Attach the CSS and JS we need to show this with and without javascript.
  153. // Without javascript we need an extra "Choose" button, and this is
  154. // hidden when we have javascript enabled.
  155. $form['#attached']['css'] = array(
  156. drupal_get_path('module', 'ajax_example') . '/ajax_example.css',
  157. );
  158. $form['#attached']['js'] = array(
  159. drupal_get_path('module', 'ajax_example') . '/ajax_example.js',
  160. );
  161. $form['description'] = array(
  162. '#type' => 'markup',
  163. '#markup' => '<div>' . t('This example demonstrates a form which dynamically creates various sections based on the configuration in the form.
  164. It deliberately allows graceful degradation to a non-javascript environment.
  165. In a non-javascript environment, the "Choose" button next to the select control
  166. is displayed; in a javascript environment it is hidden by the module CSS.
  167. <br/><br/>The basic idea here is that the form is built up based on
  168. the selection in the question_type_select field, and it is built the same
  169. whether we are in a javascript/AJAX environment or not.
  170. <br/><br/>
  171. Try the <a href="!ajax_link">AJAX version</a> and the <a href="!non_ajax_link">simulated-non-AJAX version</a>.
  172. ', array('!ajax_link' => url('examples/ajax_example/dynamic_sections'), '!non_ajax_link' => url('examples/ajax_example/dynamic_sections_no_js'))) . '</div>',
  173. );
  174. $form['question_type_select'] = array(
  175. '#type' => 'select',
  176. '#title' => t('Question style'),
  177. '#options' => drupal_map_assoc(
  178. array(
  179. t('Choose question style'),
  180. t('Multiple Choice'),
  181. t('True/False'),
  182. t('Fill-in-the-blanks'),
  183. )
  184. ),
  185. '#ajax' => array(
  186. 'wrapper' => 'questions-fieldset-wrapper',
  187. 'callback' => 'ajax_example_dynamic_sections_select_callback',
  188. ),
  189. );
  190. // The CSS for this module hides this next button if JS is enabled.
  191. $form['question_type_submit'] = array(
  192. '#type' => 'submit',
  193. '#value' => t('Choose'),
  194. '#attributes' => array('class' => array('next-button')),
  195. // No need to validate when submitting this.
  196. '#limit_validation_errors' => array(),
  197. '#validate' => array(),
  198. );
  199. // This simply allows us to demonstrate no-javascript use without
  200. // actually turning off javascript in the browser. Removing the #ajax
  201. // element turns off AJAX behaviors on that element and as a result
  202. // ajax.js doesn't get loaded.
  203. if ($no_js_use) {
  204. // Remove the #ajax from the above, so ajax.js won't be loaded.
  205. unset($form['question_type_select']['#ajax']);
  206. }
  207. // This fieldset just serves as a container for the part of the form
  208. // that gets rebuilt.
  209. $form['questions_fieldset'] = array(
  210. '#type' => 'fieldset',
  211. // These provide the wrapper referred to in #ajax['wrapper'] above.
  212. '#prefix' => '<div id="questions-fieldset-wrapper">',
  213. '#suffix' => '</div>',
  214. );
  215. if (!empty($form_state['values']['question_type_select'])) {
  216. $form['questions_fieldset']['question'] = array(
  217. '#markup' => t('Who was the first president of the U.S.?'),
  218. );
  219. $question_type = $form_state['values']['question_type_select'];
  220. switch ($question_type) {
  221. case t('Multiple Choice'):
  222. $form['questions_fieldset']['question'] = array(
  223. '#type' => 'radios',
  224. '#title' => t('Who was the first president of the United States'),
  225. '#options' => drupal_map_assoc(
  226. array(
  227. t('George Bush'),
  228. t('Adam McGuire'),
  229. t('Abraham Lincoln'),
  230. t('George Washington'),
  231. )
  232. ),
  233. );
  234. break;
  235. case t('True/False'):
  236. $form['questions_fieldset']['question'] = array(
  237. '#type' => 'radios',
  238. '#title' => t('Was George Washington the first president of the United States?'),
  239. '#options' => array(t('George Washington') => t("True"), 0 => t("False")),
  240. '#description' => t('Click "True" if you think George Washington was the first president of the United States.'),
  241. );
  242. break;
  243. case t('Fill-in-the-blanks'):
  244. $form['questions_fieldset']['question'] = array(
  245. '#type' => 'textfield',
  246. '#title' => t('Who was the first president of the United States'),
  247. '#description' => t('Please type the correct answer to the question.'),
  248. );
  249. break;
  250. }
  251. $form['questions_fieldset']['submit'] = array(
  252. '#type' => 'submit',
  253. '#value' => t('Submit your answer'),
  254. );
  255. }
  256. return $form;
  257. }
  258. /**
  259. * Validation function for ajax_example_dynamic_sections().
  260. */
  261. function ajax_example_dynamic_sections_validate($form, &$form_state) {
  262. $answer = $form_state['values']['question'];
  263. if ($answer !== t('George Washington')) {
  264. form_set_error('question', t('Wrong answer. Try again. (Hint: The right answer is "George Washington".)'));
  265. }
  266. }
  267. /**
  268. * Submit function for ajax_example_dynamic_sections().
  269. */
  270. function ajax_example_dynamic_sections_submit($form, &$form_state) {
  271. // This is only executed when a button is pressed, not when the AJAXified
  272. // select is changed.
  273. // Now handle the case of the next, previous, and submit buttons.
  274. // Only submit will result in actual submission, all others rebuild.
  275. switch ($form_state['triggering_element']['#value']) {
  276. case t('Submit your answer'):
  277. // Submit: We're done.
  278. $form_state['rebuild'] = FALSE;
  279. $answer = $form_state['values']['question'];
  280. // Special handling for the checkbox.
  281. if ($answer == 1 && $form['questions_fieldset']['question']['#type'] == 'checkbox') {
  282. $answer = $form['questions_fieldset']['question']['#title'];
  283. }
  284. if ($answer === t('George Washington')) {
  285. drupal_set_message(t('You got the right answer: @answer', array('@answer' => $answer)));
  286. }
  287. else {
  288. drupal_set_message(t('Sorry, your answer (@answer) is wrong', array('@answer' => $answer)));
  289. }
  290. $form_state['rebuild'] = FALSE;
  291. return;
  292. // Any other form element will cause rebuild of the form and present
  293. // it again.
  294. case t('Choose'):
  295. $form_state['values']['question_type_select'] = $form_state['input']['question_type_select'];
  296. // Fall through.
  297. default:
  298. $form_state['rebuild'] = TRUE;
  299. }
  300. }
  301. /**
  302. * Callback for the select element.
  303. *
  304. * This just selects and returns the questions_fieldset.
  305. */
  306. function ajax_example_dynamic_sections_select_callback($form, $form_state) {
  307. return $form['questions_fieldset'];
  308. }
  309. /**
  310. * Wizard form.
  311. *
  312. * This example is a classic wizard, where a different and sequential form
  313. * is presented on each step of the form.
  314. *
  315. * In the AJAX version, the form is replaced for each wizard section. In the
  316. * multistep version, it causes a new page load.
  317. *
  318. * @param array $form
  319. * Form API form.
  320. * @param array $form_state
  321. * Form API form.
  322. * @param bool $no_js_use
  323. * Used for this demonstration only. If true means that the form should be
  324. * built using a simulated no-javascript approach (ajax.js will not be
  325. * loaded.)
  326. *
  327. * @return array
  328. * Form array.
  329. */
  330. function ajax_example_wizard($form, &$form_state, $no_js_use = FALSE) {
  331. // Provide a wrapper around the entire form, since we'll replace the whole
  332. // thing with each submit.
  333. $form['#prefix'] = '<div id="wizard-form-wrapper">';
  334. $form['#suffix'] = '</div>';
  335. // We want to deal with hierarchical form values.
  336. $form['#tree'] = TRUE;
  337. $form['description'] = array(
  338. '#markup' => '<div>' . t('This example is a step-by-step wizard. The <a href="!ajax">AJAX version</a> does it without page reloads; the <a href="!multistep">multistep version</a> is the same code but simulates a non-javascript environment, showing it with page reloads.',
  339. array('!ajax' => url('examples/ajax_example/wizard'), '!multistep' => url('examples/ajax_example/wizard_no_js')))
  340. . '</div>',
  341. );
  342. // $form_state['storage'] has no specific drupal meaning, but it is
  343. // traditional to keep variables for multistep forms there.
  344. $step = empty($form_state['storage']['step']) ? 1 : $form_state['storage']['step'];
  345. $form_state['storage']['step'] = $step;
  346. switch ($step) {
  347. case 1:
  348. $form['step1'] = array(
  349. '#type' => 'fieldset',
  350. '#title' => t('Step 1: Personal details'),
  351. );
  352. $form['step1']['name'] = array(
  353. '#type' => 'textfield',
  354. '#title' => t('Your name'),
  355. '#default_value' => empty($form_state['values']['step1']['name']) ? '' : $form_state['values']['step1']['name'],
  356. '#required' => TRUE,
  357. );
  358. break;
  359. case 2:
  360. $form['step2'] = array(
  361. '#type' => 'fieldset',
  362. '#title' => t('Step 2: Street address info'),
  363. );
  364. $form['step2']['address'] = array(
  365. '#type' => 'textfield',
  366. '#title' => t('Your street address'),
  367. '#default_value' => empty($form_state['values']['step2']['address']) ? '' : $form_state['values']['step2']['address'],
  368. '#required' => TRUE,
  369. );
  370. break;
  371. case 3:
  372. $form['step3'] = array(
  373. '#type' => 'fieldset',
  374. '#title' => t('Step 3: City info'),
  375. );
  376. $form['step3']['city'] = array(
  377. '#type' => 'textfield',
  378. '#title' => t('Your city'),
  379. '#default_value' => empty($form_state['values']['step3']['city']) ? '' : $form_state['values']['step3']['city'],
  380. '#required' => TRUE,
  381. );
  382. break;
  383. }
  384. if ($step == 3) {
  385. $form['submit'] = array(
  386. '#type' => 'submit',
  387. '#value' => t("Submit your information"),
  388. );
  389. }
  390. if ($step < 3) {
  391. $form['next'] = array(
  392. '#type' => 'submit',
  393. '#value' => t('Next step'),
  394. '#ajax' => array(
  395. 'wrapper' => 'wizard-form-wrapper',
  396. 'callback' => 'ajax_example_wizard_callback',
  397. ),
  398. );
  399. }
  400. if ($step > 1) {
  401. $form['prev'] = array(
  402. '#type' => 'submit',
  403. '#value' => t("Previous step"),
  404. // Since all info will be discarded, don't validate on 'prev'.
  405. '#limit_validation_errors' => array(),
  406. // #submit is required to use #limit_validation_errors
  407. '#submit' => array('ajax_example_wizard_submit'),
  408. '#ajax' => array(
  409. 'wrapper' => 'wizard-form-wrapper',
  410. 'callback' => 'ajax_example_wizard_callback',
  411. ),
  412. );
  413. }
  414. // This simply allows us to demonstrate no-javascript use without
  415. // actually turning off javascript in the browser. Removing the #ajax
  416. // element turns off AJAX behaviors on that element and as a result
  417. // ajax.js doesn't get loaded.
  418. // For demonstration only! You don't need this.
  419. if ($no_js_use) {
  420. // Remove the #ajax from the above, so ajax.js won't be loaded.
  421. // For demonstration only.
  422. unset($form['next']['#ajax']);
  423. unset($form['prev']['#ajax']);
  424. }
  425. return $form;
  426. }
  427. /**
  428. * Wizard callback function.
  429. *
  430. * @param array $form
  431. * Form API form.
  432. * @param array $form_state
  433. * Form API form.
  434. *
  435. * @return array
  436. * Form array.
  437. */
  438. function ajax_example_wizard_callback($form, $form_state) {
  439. return $form;
  440. }
  441. /**
  442. * Submit function for ajax_example_wizard.
  443. *
  444. * In AJAX this is only submitted when the final submit button is clicked,
  445. * but in the non-javascript situation, it is submitted with every
  446. * button click.
  447. */
  448. function ajax_example_wizard_submit($form, &$form_state) {
  449. // Save away the current information.
  450. $current_step = 'step' . $form_state['storage']['step'];
  451. if (!empty($form_state['values'][$current_step])) {
  452. $form_state['storage']['values'][$current_step] = $form_state['values'][$current_step];
  453. }
  454. // Increment or decrement the step as needed. Recover values if they exist.
  455. if ($form_state['triggering_element']['#value'] == t('Next step')) {
  456. $form_state['storage']['step']++;
  457. // If values have already been entered for this step, recover them from
  458. // $form_state['storage'] to pre-populate them.
  459. $step_name = 'step' . $form_state['storage']['step'];
  460. if (!empty($form_state['storage']['values'][$step_name])) {
  461. $form_state['values'][$step_name] = $form_state['storage']['values'][$step_name];
  462. }
  463. }
  464. if ($form_state['triggering_element']['#value'] == t('Previous step')) {
  465. $form_state['storage']['step']--;
  466. // Recover our values from $form_state['storage'] to pre-populate them.
  467. $step_name = 'step' . $form_state['storage']['step'];
  468. $form_state['values'][$step_name] = $form_state['storage']['values'][$step_name];
  469. }
  470. // If they're done, submit.
  471. if ($form_state['triggering_element']['#value'] == t('Submit your information')) {
  472. $value_message = t('Your information has been submitted:') . ' ';
  473. foreach ($form_state['storage']['values'] as $step => $values) {
  474. $value_message .= "$step: ";
  475. foreach ($values as $key => $value) {
  476. $value_message .= "$key=$value, ";
  477. }
  478. }
  479. drupal_set_message($value_message);
  480. $form_state['rebuild'] = FALSE;
  481. return;
  482. }
  483. // Otherwise, we still have work to do.
  484. $form_state['rebuild'] = TRUE;
  485. }
  486. /**
  487. * Form with 'add more' and 'remove' buttons.
  488. *
  489. * This example shows a button to "add more" - add another textfield, and
  490. * the corresponding "remove" button.
  491. *
  492. * It works equivalently with javascript or not, and does the same basic steps
  493. * either way.
  494. *
  495. * The basic idea is that we build the form based on the setting of
  496. * $form_state['num_names']. The custom submit functions for the "add-one"
  497. * and "remove-one" buttons increment and decrement $form_state['num_names']
  498. * and then force a rebuild of the form.
  499. *
  500. * The $no_js_use argument is simply for demonstration: When set, it prevents
  501. * '#ajax' from being set, thus making the example behave as if javascript
  502. * were disabled in the browser.
  503. */
  504. function ajax_example_add_more($form, &$form_state, $no_js_use = FALSE) {
  505. $form['description'] = array(
  506. '#markup' => '<div>' . t('This example shows an add-more and a remove-last button. The <a href="!ajax">AJAX version</a> does it without page reloads; the <a href="!multistep">non-js version</a> is the same code but simulates a non-javascript environment, showing it with page reloads.',
  507. array('!ajax' => url('examples/ajax_example/add_more'), '!multistep' => url('examples/ajax_example/add_more_no_js')))
  508. . '</div>',
  509. );
  510. // Because we have many fields with the same values, we have to set
  511. // #tree to be able to access them.
  512. $form['#tree'] = TRUE;
  513. $form['names_fieldset'] = array(
  514. '#type' => 'fieldset',
  515. '#title' => t('People coming to the picnic'),
  516. // Set up the wrapper so that AJAX will be able to replace the fieldset.
  517. '#prefix' => '<div id="names-fieldset-wrapper">',
  518. '#suffix' => '</div>',
  519. );
  520. // Build the fieldset with the proper number of names. We'll use
  521. // $form_state['num_names'] to determine the number of textfields to build.
  522. if (empty($form_state['num_names'])) {
  523. $form_state['num_names'] = 1;
  524. }
  525. for ($i = 0; $i < $form_state['num_names']; $i++) {
  526. $form['names_fieldset']['name'][$i] = array(
  527. '#type' => 'textfield',
  528. '#title' => t('Name'),
  529. );
  530. }
  531. $form['names_fieldset']['add_name'] = array(
  532. '#type' => 'submit',
  533. '#value' => t('Add one more'),
  534. '#submit' => array('ajax_example_add_more_add_one'),
  535. // See the examples in ajax_example.module for more details on the
  536. // properties of #ajax.
  537. '#ajax' => array(
  538. 'callback' => 'ajax_example_add_more_callback',
  539. 'wrapper' => 'names-fieldset-wrapper',
  540. ),
  541. );
  542. if ($form_state['num_names'] > 1) {
  543. $form['names_fieldset']['remove_name'] = array(
  544. '#type' => 'submit',
  545. '#value' => t('Remove one'),
  546. '#submit' => array('ajax_example_add_more_remove_one'),
  547. '#ajax' => array(
  548. 'callback' => 'ajax_example_add_more_callback',
  549. 'wrapper' => 'names-fieldset-wrapper',
  550. ),
  551. );
  552. }
  553. $form['submit'] = array(
  554. '#type' => 'submit',
  555. '#value' => t('Submit'),
  556. );
  557. // This simply allows us to demonstrate no-javascript use without
  558. // actually turning off javascript in the browser. Removing the #ajax
  559. // element turns off AJAX behaviors on that element and as a result
  560. // ajax.js doesn't get loaded.
  561. // For demonstration only! You don't need this.
  562. if ($no_js_use) {
  563. // Remove the #ajax from the above, so ajax.js won't be loaded.
  564. if (!empty($form['names_fieldset']['remove_name']['#ajax'])) {
  565. unset($form['names_fieldset']['remove_name']['#ajax']);
  566. }
  567. unset($form['names_fieldset']['add_name']['#ajax']);
  568. }
  569. return $form;
  570. }
  571. /**
  572. * Callback for both ajax-enabled buttons.
  573. *
  574. * Selects and returns the fieldset with the names in it.
  575. */
  576. function ajax_example_add_more_callback($form, $form_state) {
  577. return $form['names_fieldset'];
  578. }
  579. /**
  580. * Submit handler for the "add-one-more" button.
  581. *
  582. * Increments the max counter and causes a rebuild.
  583. */
  584. function ajax_example_add_more_add_one($form, &$form_state) {
  585. $form_state['num_names']++;
  586. $form_state['rebuild'] = TRUE;
  587. }
  588. /**
  589. * Submit handler for the "remove one" button.
  590. *
  591. * Decrements the max counter and causes a form rebuild.
  592. */
  593. function ajax_example_add_more_remove_one($form, &$form_state) {
  594. if ($form_state['num_names'] > 1) {
  595. $form_state['num_names']--;
  596. }
  597. $form_state['rebuild'] = TRUE;
  598. }
  599. /**
  600. * Final submit handler.
  601. *
  602. * Reports what values were finally set.
  603. */
  604. function ajax_example_add_more_submit($form, &$form_state) {
  605. $output = t('These people are coming to the picnic: @names',
  606. array(
  607. '@names' => implode(', ', $form_state['values']['names_fieldset']['name']),
  608. )
  609. );
  610. drupal_set_message($output);
  611. }
  612. /**
  613. * @} End of "defgroup ajax_degradation_example".
  614. */