diff --git a/ajax_example/ajax_example.libaries.yml b/ajax_example/ajax_example.libaries.yml deleted file mode 100644 index ecfeaf1..0000000 --- a/ajax_example/ajax_example.libaries.yml +++ /dev/null @@ -1,7 +0,0 @@ -ajax_example.dropdown: - version: VERSION - css: - theme: - css/ajax_example.css: {} - js: - js/ajax_example.js: {} diff --git a/ajax_example/ajax_example.libraries.yml b/ajax_example/ajax_example.libraries.yml new file mode 100644 index 0000000..f4daca9 --- /dev/null +++ b/ajax_example/ajax_example.libraries.yml @@ -0,0 +1,7 @@ +ajax_example.library: + version: 1.x + css: + theme: + css/ajax-example-theme.css: {} + js: + js/ajax-example.js: {} diff --git a/ajax_example/ajax_example.links.menu.yml b/ajax_example/ajax_example.links.menu.yml index d5bd495..2f5aa27 100644 --- a/ajax_example/ajax_example.links.menu.yml +++ b/ajax_example/ajax_example.links.menu.yml @@ -71,6 +71,6 @@ ajax_example.dynamic-dropdown: ajax_example.dynamic-dropdown-degrades-nojava-example: title: 'Dynamic sections with graceful degradation, w/js turned off' - route_name: 'ajax_example.dynamic_sections_nojava' + route_name: 'ajax_example.dynamic_form_sections' parent: ajax_example.description weight: 10 diff --git a/ajax_example/ajax_example.routing.yml b/ajax_example/ajax_example.routing.yml index a7310c0..42977fc 100644 --- a/ajax_example/ajax_example.routing.yml +++ b/ajax_example/ajax_example.routing.yml @@ -63,12 +63,12 @@ ajax_example.dynamic_sections: requirements: _permission: 'access content' -ajax_example.dynamic_sections_nojava: - path: 'examples/ajax-example/dynamic-sections-nojava/{no_js_use}' +ajax_example.dynamic_form_sections: + path: 'examples/ajax-example/dynamic-form-sections/{nojs}' defaults: - _form: '\Drupal\ajax_example\Form\DynamicSectionsDegrades' - _title: 'Dynamic sections w/JS turned off' - no_js_use: TRUE + _form: '\Drupal\ajax_example\Form\DynamicFormSections' + _title: 'Dynamic form sections w/JS turned off' + nojs: 'nojs' requirements: _permission: 'access content' diff --git a/ajax_example/css/ajax_example.css b/ajax_example/css/ajax-example-theme.css similarity index 89% rename from ajax_example/css/ajax_example.css rename to ajax_example/css/ajax-example-theme.css index 1e94a09..357126a 100644 --- a/ajax_example/css/ajax_example.css +++ b/ajax_example/css/ajax-example-theme.css @@ -9,6 +9,10 @@ html.js .next-button { display: none; } +.button .ajax-hide-submit { + display: none; +} + /* Makes the next/choose button align to the right of the select control */ .form-item-dropdown-first, .form-item-question-type-select { diff --git a/ajax_example/js/ajax_example.js b/ajax_example/js/ajax-example.js similarity index 93% rename from ajax_example/js/ajax_example.js rename to ajax_example/js/ajax-example.js index feb9559..b3ffc3b 100644 --- a/ajax_example/js/ajax_example.js +++ b/ajax_example/js/ajax-example.js @@ -18,7 +18,7 @@ // the 'ok' button when JS is enabled. Here, for demonstration purposes, // we have AJAX disabled but JS turned on, so use this to simulate. if (!Drupal.ajax) { - $('html.js .next-button').show(); + $('.button .ajax-hide-submit').show(); } } }; diff --git a/ajax_example/src/Form/DependentDropdownDegrades.php b/ajax_example/src/Form/DependentDropdownDegrades.php index 9049cc9..0fe8528 100644 --- a/ajax_example/src/Form/DependentDropdownDegrades.php +++ b/ajax_example/src/Form/DependentDropdownDegrades.php @@ -122,7 +122,7 @@ class DependentDropdownDegrades extends FormBase { // Attach the CSS and JS we need to show this with and without javascript. // Without javascript we need an extra "Choose" button, and this is // hidden when we have javascript enabled. - $form['#attached']['library'][] = 'ajax_example/ajax_eample.dropdown'; + $form['#attached']['library'][] = 'ajax_example/ajax_example.library'; $form['dropdown_first_fieldset'] = [ '#type' => 'details', diff --git a/ajax_example/src/Form/DynamicSectionsDegrades.php b/ajax_example/src/Form/DynamicFormSections.php similarity index 63% rename from ajax_example/src/Form/DynamicSectionsDegrades.php rename to ajax_example/src/Form/DynamicFormSections.php index 6337468..a95001a 100644 --- a/ajax_example/src/Form/DynamicSectionsDegrades.php +++ b/ajax_example/src/Form/DynamicFormSections.php @@ -4,14 +4,13 @@ namespace Drupal\ajax_example\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Link; use Drupal\Core\Url; /** * Dynamically-enabled form with graceful no-JS degradation. * - * Example of a form with portions dynamically enabled or disabled, but - * with graceful degradation in the case of no javascript. + * Example of a form with portions dynamically enabled or disabled, but with + * graceful degradation in the case of no JavaScript. * * The idea here is that certain parts of the form don't need to be displayed * unless a given option is selected, but then they should be displayed and @@ -20,7 +19,7 @@ use Drupal\Core\Url; * The third $no_js_use argument is strictly for demonstrating operation * without javascript, without making the user/developer turn off javascript. */ -class DynamicSectionsDegrades extends FormBase { +class DynamicFormSections extends FormBase { /** * {@inheritdoc} @@ -31,52 +30,69 @@ class DynamicSectionsDegrades extends FormBase { /** * {@inheritdoc} + * + * @todo: URL logic doesn't work. + * @todo: library CSS and JS still don't load. */ - public function buildForm(array $form, FormStateInterface $form_state, $no_js_use = FALSE) { - $url = Url::fromUri('internal:/examples/ajax-example/dynamic-sections-no_js'); - $link = Link::fromTextAndUrl($this->t('examples/ajax-example/dynamic-sections-no-js'), $url)->toString(); - - // Prepare link for multiple arguments. - $urltwo = Url::fromUri('internal:/examples/ajax-example/dynamic-sections'); - $linktwo = Link::fromTextAndUrl($this->t('examples/ajax-example/dynamic-sections'), $urltwo)->toString(); + public function buildForm(array $form, FormStateInterface $form_state, $nojs = NULL) { // Attach the CSS and JS we need to show this with and without javascript. // Without javascript we need an extra "Choose" button, and this is // hidden when we have javascript enabled. - $form['#attached']['library'][] = 'ajax_example/ajax_eample.dropdown'; + $form = []; + $form['#attached']['library'][] = 'ajax_example/ajax_example.library'; + // Explanatory text with helpful links. + $url = Url::fromRoute('ajax_example.dynamic_form_sections'); + $url->setRouteParameter('nojs', 'ajax'); $form['description'] = [ - '#type' => 'markup', - '#markup' => $this->t('This example demonstrates a form which dynamically creates various sections based on the configuration in the form. - It deliberately allows graceful degradation to a non-javascript environment. + '#markup' => $this->t('

This example demonstrates a form which dynamically + creates various sections based on the configuration in the form.

+

It deliberately allows graceful degradation to a non-javascript environment. In a non-javascript environment, the "Choose" button next to the select control - is displayed; in a javascript environment it is hidden by the module CSS. - The basic idea here is that the form is built up based on + is displayed; in a javascript environment it is hidden by the module CSS.

+

The basic idea here is that the form is built up based on the selection in the question_type_select field, and it is built the same - whether we are in a javascript/AJAX environment or not. - - Try the @link and the @link1.', ['@link' => $linktwo, '@link1' => $link]), + whether we are in a javascript/AJAX environment or not.

+

The form is reachable through the URL path @path. This URL defaults to not + using AJAX features, in order to simulate a web browser where JavaScript + is disabled. You can add /ajax to the end of the url to show + how this form behaves when it uses AJAX to change the form elements.

+ '//, +/* [ + '@path' => Url::fromRoute('ajax_example.dynamic_form_sections')->getInternalPath(), + ':url_with_ajax' => $url, + '@path_with_ajax' => $url->getInternalPath(), + ] + */ ) ]; $form['question_type_select'] = [ + // This is our select dropdown. '#type' => 'select', '#title' => t('Question style'), + // We have a variety of form items you can use to get input from the user. '#options' => [ 'Choose question style' => 'Choose question style', 'Multiple Choice' => 'Multiple Choice', 'True/False' => 'True/False', 'Fill-in-the-blanks' => 'Fill-in-the-blanks', ], - + // The #ajax section tells the AJAX system that whenever this dropdown + // emits an event, it should call the callback and put the resulting + // content into the wrapper we specify. The questions-fieldset-wrapper is + // defined below. '#ajax' => [ 'wrapper' => 'questions-fieldset-wrapper', - 'callback' => '::prompt', + 'callback' => '::promptCallback', ], ]; // The CSS for this module hides this next button if JS is enabled. $form['question_type_submit'] = [ '#type' => 'submit', '#value' => t('Choose'), - '#attributes' => ['class' => ['next-button']], - // No need to validate when submitting this. + '#attributes' => ['class' => ['ajax-hide-submit']], + // No need to validate when submitting this. '#limit_validation_errors' => [], '#validate' => [], ]; @@ -85,19 +101,26 @@ class DynamicSectionsDegrades extends FormBase { // actually turning off javascript in the browser. Removing the #ajax // element turns off AJAX behaviors on that element and as a result // ajax.js doesn't get loaded. - if ($no_js_use) { + if ($nojs == 'nojs') { // Remove the #ajax from the above, so ajax.js won't be loaded. unset($form['question_type_select']['#ajax']); } // This fieldset just serves as a container for the part of the form - // that gets rebuilt. + // that gets rebuilt. It has a nice line around it so you can see it. $form['questions_fieldset'] = [ - '#type' => 'fieldset', - // These provide the wrapper referred to in #ajax['wrapper'] above. - '#prefix' => '
', - '#suffix' => '
', + '#type' => 'details', + '#title' => $this->t('Stuff will appear here'), + '#open' => TRUE, + // We set the ID of this fieldset to questions-fieldset-wrapper so the + // AJAX command can replace it. + '#attributes' => ['id' => 'questions-fieldset-wrapper'], ]; + + // When the AJAX request comes in, or when the user hit 'Submit' if there is + // no JavaScript, the form state will tell us what the user has selected + // from the dropdown. We can look at the value of the dropdown to determine + // which secondary form to display. if (!empty($form_state->getValue('question_type_select'))) { $form['questions_fieldset']['question'] = [ @@ -105,6 +128,8 @@ class DynamicSectionsDegrades extends FormBase { ]; $question_type = $form_state->getValue('question_type_select'); + // Build up a secondary form, based on the type of question the user + // chose. switch ($question_type) { case 'Multiple Choice': $form['questions_fieldset']['question'] = [ @@ -187,9 +212,19 @@ class DynamicSectionsDegrades extends FormBase { /** * Callback for the select element. * - * This just selects and returns the questions_fieldset. + * Since the questions_fieldset part of the form has already been built during + * the AJAX request, we can return only that part of the form to the AJAX + * request, and it will insert that part into questions-fieldset-wrapper. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * The form structure. */ - public function prompt($form, $form_state) { + public function promptCallback($form, $form_state) { return $form['questions_fieldset']; } diff --git a/ajax_example/templates/description.html.twig b/ajax_example/templates/description.html.twig index 308ed68..0878c23 100644 --- a/ajax_example/templates/description.html.twig +++ b/ajax_example/templates/description.html.twig @@ -11,7 +11,7 @@ Description text for the Ajax Example. {% set ajax_dependent_dropdown_degrade = path('ajax_example.dependent_dropdown_degrades') %} {% set ajax_dependent_dropdown_nojs = path('ajax_example.dependent_dropdown_degrades_nojava') %} {% set ajax_dynamic_dropdown = path('ajax_example.dynamic_sections') %} -{% set ajax_dynamic_dropdown_nojs = path('ajax_example.dynamic_sections_nojava') %} +{% set ajax_dynamic_dropdown_nojs = path('ajax_example.dynamic_form_sections') %} {% set ajax_wizard_example = path('ajax_example.wizard') %} {% set ajax_wizard_example_nojs = path('ajax_example.wizardnojs') %} diff --git a/ajax_example/tests/src/Functional/AjaxExampleMenuTest.php b/ajax_example/tests/src/Functional/AjaxExampleMenuTest.php index ab5b516..b6acc0b 100644 --- a/ajax_example/tests/src/Functional/AjaxExampleMenuTest.php +++ b/ajax_example/tests/src/Functional/AjaxExampleMenuTest.php @@ -48,7 +48,7 @@ class AjaxExampleMenuTest extends BrowserTestBase { 'ajax_example.dependent_dropdown_degrades' => ['Choose', 'OK'], 'ajax_example.dependent_dropdown_degrades_nojava' => ['Choose', 'OK'], 'ajax_example.dynamic_sections' => ['Choose', 'OK'], - 'ajax_example.dynamic_sections_nojava' => ['Choose'], + 'ajax_example.dynamic_form_sections' => ['Choose'], 'ajax_example.wizard' => ['Next step'], 'ajax_example.wizardnojs' => ['Next step'], 'ajax_example.autocomplete_user' => ['Submit'],