added example for developpers module
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
name: 'AJAX Example'
|
||||
type: module
|
||||
description: 'An example module showing how to use Drupal AJAX forms.'
|
||||
package: 'Example modules'
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- drupal:node
|
||||
- examples:examples
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-12-17
|
||||
version: '8.x-1.x-dev'
|
||||
core: '8.x'
|
||||
project: 'examples'
|
||||
datestamp: 1513537386
|
||||
@@ -0,0 +1,7 @@
|
||||
ajax_example.library:
|
||||
version: 1.x
|
||||
css:
|
||||
base:
|
||||
css/ajax-example-base.css: {}
|
||||
js:
|
||||
js/ajax-example.js: {}
|
||||
@@ -0,0 +1,72 @@
|
||||
ajax_example.description:
|
||||
title: 'AJAX Example'
|
||||
route_name: 'ajax_example.description'
|
||||
expanded: TRUE
|
||||
|
||||
ajax_example.simplest:
|
||||
title: 'Simplest AJAX example'
|
||||
route_name: 'ajax_example.simplest'
|
||||
parent: ajax_example.description
|
||||
weight: 0
|
||||
|
||||
ajax_example.submit-driven:
|
||||
title: 'Submit-driven AJAX'
|
||||
route_name: 'ajax_example.submit_driven_ajax'
|
||||
parent: ajax_example.description
|
||||
weight: 1
|
||||
|
||||
ajax_example.render-link:
|
||||
title: 'AJAX link in a render array'
|
||||
route_name: 'ajax_example.ajax_link_render'
|
||||
parent: ajax_example.description
|
||||
weight: 2
|
||||
|
||||
ajax_example.wizard-example:
|
||||
title: 'Wizard example'
|
||||
route_name: 'ajax_example.wizard'
|
||||
parent: ajax_example.description
|
||||
weight: 2
|
||||
|
||||
ajax_example.wizard-examplenojs:
|
||||
title: 'Wizard example w/JS turned off'
|
||||
route_name: 'ajax_example.wizardnojs'
|
||||
parent: ajax_example.description
|
||||
weight: 3
|
||||
|
||||
ajax_example.autocomplete-user:
|
||||
title: 'Autocomplete user with entity_autocomplete'
|
||||
route_name: 'ajax_example.autocomplete_user'
|
||||
parent: ajax_example.description
|
||||
weight: 4
|
||||
|
||||
ajax_example.autotextfields:
|
||||
title: 'Generate textfields'
|
||||
route_name: 'ajax_example.autotextfields'
|
||||
parent: ajax_example.description
|
||||
weight: 5
|
||||
|
||||
ajax_example.dependent-dropdown:
|
||||
title: 'Dependent dropdown'
|
||||
route_name: 'ajax_example.dependent_dropdown'
|
||||
parent: ajax_example.description
|
||||
weight: 6
|
||||
ajax_example.dependent-dropdown-nojs:
|
||||
title: 'Dependent dropdown w/ no JS'
|
||||
route_name: 'ajax_example.dependent_dropdown'
|
||||
route_parameters:
|
||||
nojs: nojs
|
||||
parent: ajax_example.description
|
||||
weight: 6
|
||||
|
||||
ajax_example.dynamic-form-sections:
|
||||
title: 'Dynamic form sections'
|
||||
route_name: 'ajax_example.dynamic_form_sections'
|
||||
parent: ajax_example.description
|
||||
weight: 10
|
||||
ajax_example.dynamic-form-sections-nojs:
|
||||
title: 'Dynamic form sections w/ no JS'
|
||||
route_name: 'ajax_example.dynamic_form_sections'
|
||||
route_parameters:
|
||||
nojs: nojs
|
||||
parent: ajax_example.description
|
||||
weight: 10
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* AJAX Examples module file with basic examples.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup ajax_example Example: AJAX
|
||||
* @ingroup examples
|
||||
* @{
|
||||
* These examples show basic AJAX concepts.
|
||||
*
|
||||
* General documentation is available at
|
||||
* @link ajax AJAX Framework documentation @endlink and at the
|
||||
* @link http://drupal.org/node/752056 AJAX Forms handbook page @endlink.
|
||||
*
|
||||
* The several examples here demonstrate basic AJAX usage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @} End of "defgroup ajax_example".
|
||||
*/
|
||||
@@ -0,0 +1,96 @@
|
||||
ajax_example.description:
|
||||
path: 'examples/ajax-example'
|
||||
defaults:
|
||||
_controller: '\Drupal\ajax_example\Controller\AjaxExampleController::description'
|
||||
_title: 'AJAX Example'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.simplest:
|
||||
path: 'examples/ajax-example/simplest'
|
||||
defaults:
|
||||
_form: '\Drupal\ajax_example\Form\Simplest'
|
||||
_title: 'Simplest AJAX example'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.autotextfields:
|
||||
path: 'examples/ajax-example/autotextfields'
|
||||
defaults:
|
||||
_form: '\Drupal\ajax_example\Form\Autotextfields'
|
||||
_title: 'Generate textfields'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.submit_driven_ajax:
|
||||
path: 'examples/ajax-example/submit-driven-ajax'
|
||||
defaults:
|
||||
_form: '\Drupal\ajax_example\Form\SubmitDriven'
|
||||
_title: 'Submit-driven AJAX'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.dependent_dropdown:
|
||||
path: 'examples/ajax-example/dependent-dropdown/{nojs}'
|
||||
defaults:
|
||||
_form: '\Drupal\ajax_example\Form\DependentDropdown'
|
||||
_title: 'Dependent dropdown'
|
||||
nojs: ajax
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.dynamic_form_sections:
|
||||
path: 'examples/ajax-example/dynamic-form-sections/{nojs}'
|
||||
defaults:
|
||||
_form: '\Drupal\ajax_example\Form\DynamicFormSections'
|
||||
_title: 'Dynamic form sections'
|
||||
nojs: 'ajax'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.wizard:
|
||||
path: 'examples/ajax-example/wizard'
|
||||
defaults:
|
||||
_form: '\Drupal\ajax_example\Form\Wizard'
|
||||
_title: 'Wizard with graceful degradation'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.wizardnojs:
|
||||
path: 'examples/ajax-example/wizard-nojs/{no_js_use}'
|
||||
defaults:
|
||||
_form: '\Drupal\ajax_example\Form\Wizard'
|
||||
_title: 'Wizard with graceful degradation, w/JS turned off'
|
||||
no_js_use: TRUE
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.ajax_link_render:
|
||||
path: 'examples/ajax-example/ajax-link-renderable'
|
||||
defaults:
|
||||
_controller: '\Drupal\ajax_example\Controller\AjaxExampleController::renderLinkRenderableArray'
|
||||
_title: 'AJAX link from a render array'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
# This route is for an AJAX callback. It is used by the AJAX system on
|
||||
# ajax_example.ajax_link_render. It has a {nojs} parameter, which gives us
|
||||
# a way to know whether the request is an AJAX request or is from some other
|
||||
# source.
|
||||
ajax_example.ajax_link_callback:
|
||||
path: 'examples/ajax-example/ajax-link-callback/{nojs}'
|
||||
defaults:
|
||||
_controller: '\Drupal\ajax_example\Controller\AjaxExampleController::ajaxLinkCallback'
|
||||
# We provide a default value for {nojs}, so that it can be an optional
|
||||
# parameter.
|
||||
nojs: 'nojs'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
|
||||
ajax_example.autocomplete_user:
|
||||
path: 'examples/ajax_example/user_autocomplete'
|
||||
defaults:
|
||||
_form: '\Drupal\ajax_example\Form\EntityAutocomplete'
|
||||
_title: 'Autocomplete users with entity_autocomplete.'
|
||||
requirements:
|
||||
_permission: 'access content'
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* @file
|
||||
* CSS for ajax_example.
|
||||
*/
|
||||
|
||||
/* Put some buttons inline, for nicer formatting. */
|
||||
.ajax-example-inline {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file
|
||||
* JavaScript for ajax_example.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
// Re-enable form elements that are disabled for non-ajax situations.
|
||||
Drupal.behaviors.enableFormItemsForAjaxForms = {
|
||||
attach: function () {
|
||||
// If ajax is enabled, we want to hide items that are marked as hidden in
|
||||
// our example.
|
||||
if (Drupal.ajax) {
|
||||
$('.ajax-example-hide').hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\ajax_example\Controller;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\AppendCommand;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\examples\Utility\DescriptionTemplateTrait;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Controller routines for AJAX example routes.
|
||||
*/
|
||||
class AjaxExampleController extends ControllerBase {
|
||||
|
||||
use DescriptionTemplateTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getModuleName() {
|
||||
return 'ajax_example';
|
||||
}
|
||||
|
||||
/**
|
||||
* Demonstrates a clickable AJAX-enabled link using the 'use-ajax' class.
|
||||
*
|
||||
* Because of the 'use-ajax' class applied here, the link submission is done
|
||||
* without a page refresh.
|
||||
*
|
||||
* When using the AJAX framework outside the context of a form or a renderable
|
||||
* array of type 'link', you have to include ajax.js explicitly.
|
||||
*
|
||||
* @return array
|
||||
* Form API array.
|
||||
*
|
||||
* @ingroup ajax_example
|
||||
*/
|
||||
public function renderLinkRenderableArray() {
|
||||
$build['my_div'] = [
|
||||
'#markup' => $this->t('
|
||||
The link below has been rendered as an element with the #ajax property, so if
|
||||
javascript is enabled, ajax.js will try to submit it via an AJAX call instead
|
||||
of a normal page load. The URL also contains the "/nojs/" magic string, which
|
||||
is stripped if javascript is enabled, allowing the server code to tell by the
|
||||
URL whether JS was enabled or not, letting it do different things based on that.'),
|
||||
];
|
||||
// We'll add a nice border element for our demo.
|
||||
$build['ajax_link'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('This is the AJAX link'),
|
||||
'#open' => TRUE,
|
||||
];
|
||||
// We build the AJAX link.
|
||||
$build['ajax_link']['link'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Click me'),
|
||||
// We have to ensure that Drupal's Ajax system is loaded.
|
||||
'#attached' => ['library' => ['core/drupal.ajax']],
|
||||
// We add the 'use-ajax' class so that Drupal's AJAX system can spring
|
||||
// into action.
|
||||
'#attributes' => ['class' => ['use-ajax']],
|
||||
// The URL for this link element is the callback. In our case, it's route
|
||||
// ajax_example.ajax_link_callback, which maps to ajaxLinkCallback()
|
||||
// below. The route has a /{nojs} section, which is how the callback can
|
||||
// know whether the request was made by AJAX or some other means where
|
||||
// JavaScript won't be able to handle the result. If the {nojs} part of
|
||||
// the path is replaced with 'ajax', then the request was made by AJAX.
|
||||
'#url' => Url::fromRoute('ajax_example.ajax_link_callback', ['nojs' => 'ajax']),
|
||||
];
|
||||
// We provide a DIV that AJAX can append some text into.
|
||||
$build['ajax_link']['destination'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => ['id' => ['ajax-example-destination-div']],
|
||||
];
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for link example.
|
||||
*
|
||||
* Takes different logic paths based on whether Javascript was enabled.
|
||||
* If $type == 'ajax', it tells this function that ajax.js has rewritten
|
||||
* the URL and thus we are doing an AJAX and can return an array of commands.
|
||||
*
|
||||
* @param string $nojs
|
||||
* Either 'ajax' or 'nojs. Type is simply the normal URL argument to this
|
||||
* URL.
|
||||
*
|
||||
* @return string|array
|
||||
* If $type == 'ajax', returns an array of AJAX Commands.
|
||||
* Otherwise, just returns the content, which will end up being a page.
|
||||
*/
|
||||
public function ajaxLinkCallback($nojs = 'ajax') {
|
||||
// Determine whether the request is coming from AJAX or not.
|
||||
if ($nojs == 'ajax') {
|
||||
$output = $this->t("This is some content delivered via AJAX");
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new AppendCommand('#ajax-example-destination-div', $output));
|
||||
|
||||
// See ajax_example_advanced.inc for more details on the available
|
||||
// commands and how to use them.
|
||||
// $page = array('#type' => 'ajax', '#commands' => $commands);
|
||||
// ajax_deliver($response);
|
||||
return $response;
|
||||
}
|
||||
$response = new Response($this->t("This is some content delivered via a page load."));
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\ajax_example\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Show textfields based on AJAX-enabled checkbox clicks.
|
||||
*
|
||||
* @ingroup ajax_example
|
||||
*/
|
||||
class Autotextfields extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'ajax_example_autotextfields';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This form has two checkboxes which the user can check in order to then
|
||||
* reveal the first and/or last name text fields.
|
||||
*
|
||||
* We could perform this behavior with #states. We might not want to if, for
|
||||
* instance, we wanted to require a name, but let the user choose whether
|
||||
* to enter first or last or both.
|
||||
*
|
||||
* For all the requests this class gets, the buildForm() method will always be
|
||||
* called. If an AJAX request comes in, the form state will be set to the
|
||||
* state the user changed that caused the AJAX request. So if the user enabled
|
||||
* one of our checkboxes, it will be checked in $form_state.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['description'] = [
|
||||
'#type' => 'item',
|
||||
'#markup' => $this->t('This form demonstrates changing the status of form elements through AJAX requests.'),
|
||||
];
|
||||
$form['ask_first_name'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Ask me my first name'),
|
||||
'#ajax' => [
|
||||
'callback' => '::textfieldsCallback',
|
||||
'wrapper' => 'textfields-container',
|
||||
'effect' => 'fade',
|
||||
],
|
||||
];
|
||||
$form['ask_last_name'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Ask me my last name'),
|
||||
'#ajax' => [
|
||||
'callback' => '::textfieldsCallback',
|
||||
'wrapper' => 'textfields-container',
|
||||
'effect' => 'fade',
|
||||
],
|
||||
];
|
||||
|
||||
// Wrap textfields in a container. This container will be replaced through
|
||||
// AJAX.
|
||||
$form['textfields_container'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => ['id' => 'textfields-container'],
|
||||
];
|
||||
$form['textfields_container']['textfields'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t("Generated text fields for first and last name"),
|
||||
'#description' => t('This is where we put automatically generated textfields'),
|
||||
];
|
||||
|
||||
// This form is rebuilt on all requests, so whether or not the request comes
|
||||
// from AJAX, we should rebuild everything based on the form state.
|
||||
// Checkbox values are expressed as 1 or 0, so we have to be sure to compare
|
||||
// type as well as value.
|
||||
if ($form_state->getValue('ask_first_name', NULL) === 1) {
|
||||
$form['textfields_container']['textfields']['first_name'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('First Name'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
}
|
||||
if ($form_state->getValue('ask_last_name', NULL) === 1) {
|
||||
$form['textfields_container']['textfields']['last_name'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Last Name'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
}
|
||||
|
||||
$form['textfields_container']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Click Me'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
drupal_set_message(
|
||||
$this->t('Submit handler: First name: @first_name Last name: @last_name',
|
||||
[
|
||||
'@first_name' => $form_state->getValue('first_name', 'n/a'),
|
||||
'@last_name' => $form_state->getValue('last_name', 'n/a'),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for ajax_example_autotextfields.
|
||||
*
|
||||
* Selects the piece of the form we want to use as replacement markup and
|
||||
* returns it as a form (renderable array).
|
||||
*/
|
||||
public function textfieldsCallback($form, FormStateInterface $form_state) {
|
||||
return $form['textfields_container'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\ajax_example\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Link;
|
||||
|
||||
/**
|
||||
* Re-populate a dropdown based on form state.
|
||||
*/
|
||||
class DependentDropdown extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'ajax_example_dependentdropdown';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* The $nojs parameter is specified as a path parameter on the route.
|
||||
*
|
||||
* @see ajax_example.routing.yml
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $nojs = NULL) {
|
||||
// Add our CSS and tiny JS to hide things when they should be hidden.
|
||||
$form['#attached']['library'][] = 'ajax_example/ajax_example.library';
|
||||
|
||||
// Explanatory text with helpful links.
|
||||
$form['info'] = [
|
||||
'#markup' =>
|
||||
$this->t('<p>Like other examples in this module, this form has a path that
|
||||
can be modified with /nojs to simulate its behavior without JavaScript.
|
||||
</p><ul>
|
||||
<li>@try_it_without_ajax</li>
|
||||
<li>@try_it_with_ajax</li>
|
||||
</ul>',
|
||||
[
|
||||
'@try_it_without_ajax' => Link::createFromRoute(
|
||||
$this->t('Try it without AJAX'),
|
||||
'ajax_example.dependent_dropdown', ['nojs' => 'nojs'])
|
||||
->toString(),
|
||||
'@try_it_with_ajax' => Link::createFromRoute(
|
||||
$this->t('Try it with AJAX'),
|
||||
'ajax_example.dependent_dropdown')
|
||||
->toString(),
|
||||
]
|
||||
),
|
||||
];
|
||||
|
||||
// Our first dropdown lets us select a family of instruments: String,
|
||||
// Woodwind, Brass, or Percussion.
|
||||
$instrument_family_options = static::getFirstDropdownOptions();
|
||||
// When the AJAX request occurs, this form will be build in order to process
|
||||
// form state before the AJAX callback is called. We can use this
|
||||
// opportunity to populate the form as we wish based on the changes to the
|
||||
// form that caused the AJAX request. If the user caused the AJAX request,
|
||||
// then it would have been setting a value for instrument_family_options.
|
||||
// So if there's a value in that dropdown before we build it here, we grab
|
||||
// it's value to help us build the specific instrument dropdown. Otherwise
|
||||
// we can just use the value of the first item as the default value.
|
||||
if (empty($form_state->getValue('instrument_family_dropdown'))) {
|
||||
// Use a default value.
|
||||
$selected_family = key($instrument_family_options);
|
||||
}
|
||||
else {
|
||||
// Get the value if it already exists.
|
||||
$selected_family = $form_state->getValue('instrument_family_dropdown');
|
||||
}
|
||||
|
||||
$form['instrument_family_fieldset'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Choose an instrument family'),
|
||||
];
|
||||
$form['instrument_family_fieldset']['instrument_family_dropdown'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Instrument Type'),
|
||||
'#options' => $instrument_family_options,
|
||||
'#default_value' => $selected_family,
|
||||
// Bind an ajax callback to the change event (which is the default for the
|
||||
// select form type) of the first dropdown. It will replace the second
|
||||
// dropdown when rebuilt.
|
||||
'#ajax' => [
|
||||
// When 'event' occurs, Drupal will perform an ajax request in the
|
||||
// background. Usually the default value is sufficient (eg. change for
|
||||
// select elements), but valid values include any jQuery event,
|
||||
// most notably 'mousedown', 'blur', and 'submit'.
|
||||
'callback' => '::instrumentDropdownCallback',
|
||||
'wrapper' => 'instrument-fieldset-container',
|
||||
],
|
||||
];
|
||||
// Since we don't know if the user has js or not, we always need to output
|
||||
// this element, then hide it with with css if javascript is enabled.
|
||||
$form['instrument_family_fieldset']['choose_family'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Choose'),
|
||||
'#attributes' => ['class' => ['ajax-example-hide', 'ajax-example-inline']],
|
||||
];
|
||||
// We are using the path parameter $nojs to signal when to simulate the
|
||||
// the user turning off JavaScript. We'll remove all the AJAX elements. This
|
||||
// is not required, and is here so that we can demonstrate a graceful
|
||||
// fallback without having to turn off JavaScript.
|
||||
if ($nojs == 'nojs') {
|
||||
// Removing the #ajax element tells the system not to use AJAX.
|
||||
unset($form['instrument_family_fieldset']['instrument_family_dropdown']['#ajax']);
|
||||
// Removing the ajax-example-hide class from the Choose button ensures
|
||||
// that our JavaScript won't hide it.
|
||||
unset($form['instrument_family_fieldset']['choose_family']['#attributes']);
|
||||
}
|
||||
|
||||
// Since we're managing state for this whole fieldset (both the dropdown
|
||||
// and enabling the Submit button), we want to replace the whole thing
|
||||
// on AJAX requests. That's why we put it in this container.
|
||||
$form['instrument_fieldset_container'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => ['id' => 'instrument-fieldset-container'],
|
||||
];
|
||||
// Build the instrument field set.
|
||||
$form['instrument_fieldset_container']['instrument_fieldset'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Choose an instrument'),
|
||||
];
|
||||
$form['instrument_fieldset_container']['instrument_fieldset']['instrument_dropdown'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $instrument_family_options[$selected_family] . ' ' . $this->t('Instruments'),
|
||||
// When the form is rebuilt during ajax processing, the $selected_family
|
||||
// variable will now have the new value and so the options will change.
|
||||
'#options' => static::getSecondDropdownOptions($selected_family),
|
||||
'#default_value' => !empty($form_state->getValue('instrument_dropdown')) ? $form_state->getValue('instrument_dropdown') : '',
|
||||
];
|
||||
$form['instrument_fieldset_container']['instrument_fieldset']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Submit'),
|
||||
];
|
||||
// We might normally use #state to disable the instrument fields based on
|
||||
// the instrument family fields. But since the premise is that we don't have
|
||||
// JavaScript running, #state won't work either. We have to set up the state
|
||||
// of the instrument fieldset here, based on the selected instrument family.
|
||||
if ($selected_family == 'none') {
|
||||
$form['instrument_fieldset_container']['instrument_fieldset']['instrument_dropdown']['#title'] =
|
||||
$this->t('You must choose an instrument family.');
|
||||
$form['instrument_fieldset_container']['instrument_fieldset']['instrument_dropdown']['#disabled'] = TRUE;
|
||||
$form['instrument_fieldset_container']['instrument_fieldset']['submit']['#disabled'] = TRUE;
|
||||
}
|
||||
else {
|
||||
$form['instrument_fieldset_container']['instrument_fieldset']['instrument_dropdown']['#disabled'] = FALSE;
|
||||
$form['instrument_fieldset_container']['instrument_fieldset']['submit']['#disabled'] = FALSE;
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$trigger = (string) $form_state->getTriggeringElement()['#value'];
|
||||
switch ($trigger) {
|
||||
case 'Submit':
|
||||
// Submit: We're done.
|
||||
drupal_set_message($this->t('Your values have been submitted. Instrument family: @family, Instrument: @instrument', [
|
||||
'@family' => $form_state->getValue('instrument_family_dropdown'),
|
||||
'@instrument' => $form_state->getValue('instrument_dropdown'),
|
||||
]));
|
||||
return;
|
||||
}
|
||||
// 'Choose' or anything else will cause rebuild of the form and present
|
||||
// it again.
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a new dropdown based on the AJAX call.
|
||||
*
|
||||
* This callback will occur *after* the form has been rebuilt by buildForm().
|
||||
* Since that's the case, the form should contain the right values for
|
||||
* instrument_dropdown.
|
||||
*
|
||||
* @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 portion of the render structure that will replace the
|
||||
* instrument-dropdown-replace form element.
|
||||
*/
|
||||
public function instrumentDropdownCallback(array $form, FormStateInterface $form_state) {
|
||||
return $form['instrument_fieldset_container'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to populate the first dropdown.
|
||||
*
|
||||
* This would normally be pulling data from the database.
|
||||
*
|
||||
* @return array
|
||||
* Dropdown options.
|
||||
*/
|
||||
public static function getFirstDropdownOptions() {
|
||||
return [
|
||||
'none' => 'none',
|
||||
'String' => 'String',
|
||||
'Woodwind' => 'Woodwind',
|
||||
'Brass' => 'Brass',
|
||||
'Percussion' => 'Percussion',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to populate the second dropdown.
|
||||
*
|
||||
* This would normally be pulling data from the database.
|
||||
*
|
||||
* @param string $key
|
||||
* This will determine which set of options is returned.
|
||||
*
|
||||
* @return array
|
||||
* Dropdown options
|
||||
*/
|
||||
public static function getSecondDropdownOptions($key = '') {
|
||||
switch ($key) {
|
||||
case 'String':
|
||||
$options = [
|
||||
'Violin' => 'Violin',
|
||||
'Viola' => 'Viola',
|
||||
'Cello' => 'Cello',
|
||||
'Double Bass' => 'Double Bass',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'Woodwind':
|
||||
$options = [
|
||||
'Flute' => 'Flute',
|
||||
'Clarinet' => 'Clarinet',
|
||||
'Oboe' => 'Oboe',
|
||||
'Bassoon' => 'Bassoon',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'Brass':
|
||||
$options = [
|
||||
'Trumpet' => 'Trumpet',
|
||||
'Trombone' => 'Trombone',
|
||||
'French Horn' => 'French Horn',
|
||||
'Euphonium' => 'Euphonium',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'Percussion':
|
||||
$options = [
|
||||
'Bass Drum' => 'Bass Drum',
|
||||
'Timpani' => 'Timpani',
|
||||
'Snare Drum' => 'Snare Drum',
|
||||
'Tambourine' => 'Tambourine',
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
$options = ['none' => 'none'];
|
||||
break;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\ajax_example\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Link;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* configured.
|
||||
*
|
||||
* The third $no_js_use argument is strictly for demonstrating operation
|
||||
* without JavaScript, without making the user/developer turn off JavaScript.
|
||||
*/
|
||||
class DynamicFormSections extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'ajax_example_dynamicsectiondegrades';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $nojs = NULL) {
|
||||
// Add our CSS and tiny JS to hide things when they should be hidden.
|
||||
$form['#attached']['library'][] = 'ajax_example/ajax_example.library';
|
||||
|
||||
// Explanatory text with helpful links.
|
||||
$form['info'] = [
|
||||
'#markup' =>
|
||||
$this->t('<p>Like other examples in this module, this form has a path that
|
||||
can be modified with /nojs to simulate its behavior without JavaScript.
|
||||
</p><ul>
|
||||
<li>@try_it_without_ajax</li>
|
||||
<li>@try_it_with_ajax</li>
|
||||
</ul>',
|
||||
[
|
||||
'@try_it_without_ajax' => Link::createFromRoute(
|
||||
$this->t('Try it without AJAX'),
|
||||
'ajax_example.dynamic_form_sections', ['nojs' => 'nojs'])
|
||||
->toString(),
|
||||
'@try_it_with_ajax' => Link::createFromRoute(
|
||||
$this->t('Try it with AJAX'),
|
||||
'ajax_example.dynamic_form_sections')
|
||||
->toString(),
|
||||
]
|
||||
),
|
||||
];
|
||||
|
||||
$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' => '::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' => ['ajax-example-inline']],
|
||||
// No need to validate when submitting this.
|
||||
'#limit_validation_errors' => [],
|
||||
'#validate' => [],
|
||||
];
|
||||
|
||||
// This section allows us to demonstrate no-AJAX use without turning off
|
||||
// javascript in the browser.
|
||||
if ($nojs != 'nojs') {
|
||||
// Allow JavaScript to hide the choose button if we're using AJAX.
|
||||
$form['question_type_submit']['#attributes']['class'][] = 'ajax-example-hide';
|
||||
}
|
||||
else {
|
||||
// Remove #ajax from the above, so it won't perform AJAX behaviors.
|
||||
unset($form['question_type_select']['#ajax']);
|
||||
}
|
||||
|
||||
// This fieldset just serves as a container for the part of the form
|
||||
// that gets rebuilt. It has a nice line around it so you can see it.
|
||||
$form['questions_fieldset'] = [
|
||||
'#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.
|
||||
$question_type = $form_state->getValue('question_type_select');
|
||||
if (!empty($question_type) && $question_type !== 'Choose question style') {
|
||||
|
||||
$form['questions_fieldset']['question'] = [
|
||||
'#markup' => t('Who was the first president of the U.S.?'),
|
||||
];
|
||||
|
||||
// Build up a secondary form, based on the type of question the user
|
||||
// chose.
|
||||
switch ($question_type) {
|
||||
case 'Multiple Choice':
|
||||
$form['questions_fieldset']['question'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Who was the first president of the United States'),
|
||||
'#options' => [
|
||||
'George Bush' => 'George Bush',
|
||||
'Adam McGuire' => 'Adam McGuire',
|
||||
'Abraham Lincoln' => 'Abraham Lincoln',
|
||||
'George Washington' => 'George Washington',
|
||||
],
|
||||
|
||||
];
|
||||
break;
|
||||
|
||||
case 'True/False':
|
||||
$form['questions_fieldset']['question'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Was George Washington the first president of the United States?'),
|
||||
'#options' => [
|
||||
'George Washington' => 'True',
|
||||
0 => 'False',
|
||||
],
|
||||
'#description' => $this->t('Click "True" if you think George Washington was the first president of the United States.'),
|
||||
];
|
||||
break;
|
||||
|
||||
case 'Fill-in-the-blanks':
|
||||
$form['questions_fieldset']['question'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Who was the first president of the United States'),
|
||||
'#description' => $this->t('Please type the correct answer to the question.'),
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
$form['questions_fieldset']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Submit your answer'),
|
||||
];
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Final submit handler.
|
||||
*
|
||||
* Reports what values were finally set.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// This is only executed when a button is pressed, not when the AJAXfield
|
||||
// select is changed.
|
||||
// Now handle the case of the next, previous, and submit buttons.
|
||||
// Only submit will result in actual submission, all others rebuild.
|
||||
if ($form_state->getValue('question_type_submit') == 'Choose') {
|
||||
$form_state->setValue('question_type_select', $form_state->getUserInput()['question_type_select']);
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
if ($form_state->getValue('submit') == 'Submit your answer') {
|
||||
$form_state->setRebuild(FALSE);
|
||||
$answer = $form_state->getValue('question');
|
||||
// Special handling for the checkbox.
|
||||
if ($answer == 1 && $form['questions_fieldset']['question']['#type'] == 'checkbox') {
|
||||
$answer = $form['questions_fieldset']['question']['#title'];
|
||||
}
|
||||
if ($answer == $this->t('George Washington')) {
|
||||
drupal_set_message($this->t('You got the right answer: @answer', ['@answer' => $answer]));
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('Sorry, your answer (@answer) is wrong', ['@answer' => $answer]));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Sets the form to be rebuilt after processing.
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the select element.
|
||||
*
|
||||
* 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 promptCallback(array $form, FormStateInterface $form_state) {
|
||||
return $form['questions_fieldset'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\ajax_example\Form;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* A simple autocomplete form which looks up usernames.
|
||||
*
|
||||
* @ingroup ajax_example
|
||||
*/
|
||||
class EntityAutocomplete implements FormInterface, ContainerInjectionInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity type manager service.
|
||||
*
|
||||
* We need this for the submit handler.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Container injection factory.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* The service discovery container.
|
||||
*
|
||||
* @return self
|
||||
* The form object.
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
$form = new static(
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
$form->setStringTranslation($container->get('string_translation'));
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager service.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'ajax_example_autocomplete_user';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['info'] = [
|
||||
'#markup' => '<div>' . t("This example uses the entity_autocomplete form "
|
||||
. "element to select users. You'll need a few users on your system for "
|
||||
. "it to make sense.") . '</div>',
|
||||
];
|
||||
|
||||
// Here we use the delightful entity_autocomplete form element. It allows us
|
||||
// to consistently select entities. See https://www.drupal.org/node/2418529.
|
||||
$form['users'] = [
|
||||
// A type of entity_autocomplete lets Drupal know it should autocomplete
|
||||
// entities.
|
||||
'#type' => 'entity_autocomplete',
|
||||
// We can specify entity types to autocomplete.
|
||||
'#target_type' => 'user',
|
||||
// Specifying #tags as TRUE allows for multiple selections, separated by
|
||||
// commas.
|
||||
'#tags' => TRUE,
|
||||
'#title' => t('Choose a user. Separate with commas.'),
|
||||
];
|
||||
|
||||
$form['actions'] = [
|
||||
'#type' => 'actions',
|
||||
];
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Submit'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Here we validate and signal an error if there are no users selected.
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$state_users = $form_state->getValue('users');
|
||||
if (empty($state_users)) {
|
||||
$form_state->setErrorByName('users', 'There were no users selected.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* On submit, show the user the names of the users they selected.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$state_users = $form_state->getValue('users');
|
||||
$users = [];
|
||||
foreach ($state_users as $state_user) {
|
||||
$uid = $state_user['target_id'];
|
||||
$users[] = $this->entityTypeManager->getStorage('user')->load($uid)->getUsername();
|
||||
}
|
||||
drupal_set_message('These are your users: ' . implode(' ', $users));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\ajax_example\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* A relatively simple AJAX demonstration form.
|
||||
*/
|
||||
class Simplest extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'ajax_example_simplest';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['changethis'] = [
|
||||
'#title' => $this->t("Choose something and explain why"),
|
||||
'#type' => 'select',
|
||||
'#options' => [
|
||||
'one' => 'one',
|
||||
'two' => 'two',
|
||||
'three' => 'three',
|
||||
],
|
||||
'#ajax' => [
|
||||
// #ajax has two required keys: callback and wrapper.
|
||||
// 'callback' is a function that will be called when this element
|
||||
// changes.
|
||||
'callback' => '::promptCallback',
|
||||
// 'wrapper' is the HTML id of the page element that will be replaced.
|
||||
'wrapper' => 'replace-textfield-container',
|
||||
],
|
||||
];
|
||||
|
||||
// The 'replace-textfield-container' container will be replaced whenever
|
||||
// 'changethis' is updated.
|
||||
$form['replace_textfield_container'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => ['id' => 'replace-textfield-container'],
|
||||
];
|
||||
$form['replace_textfield_container']['replace_textfield'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t("Why"),
|
||||
];
|
||||
|
||||
// An AJAX request calls the form builder function for every change.
|
||||
// We can change how we build the form based on $form_state.
|
||||
$value = $form_state->getValue('changethis');
|
||||
// The getValue() method returns NULL by default if the form element does
|
||||
// not exist. It won't exist yet if we're building it for the first time.
|
||||
if ($value !== NULL) {
|
||||
$form['replace_textfield_container']['replace_textfield']['#description'] =
|
||||
$this->t("Say why you chose '@value'", ['@value' => $value]);
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// No-op. Our form doesn't need a submit handler, because the form is never
|
||||
// submitted. We add the method here so we fulfill FormInterface.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles switching the available regions based on the selected theme.
|
||||
*/
|
||||
public function promptCallback($form, FormStateInterface $form_state) {
|
||||
return $form['replace_textfield_container'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\ajax_example\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Submit a form without a page reload.
|
||||
*/
|
||||
class SubmitDriven extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'ajax_example_autotextfields';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
// This container wil be replaced by AJAX.
|
||||
$form['container'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => ['id' => 'box-container'],
|
||||
];
|
||||
// The box contains some markup that we can change on a submit request.
|
||||
$form['container']['box'] = [
|
||||
'#type' => 'markup',
|
||||
'#markup' => '<h1>Initial markup for box</h1>',
|
||||
];
|
||||
|
||||
$form['submit'] = [
|
||||
'#type' => 'submit',
|
||||
// The AJAX handler will call our callback, and will replace whatever page
|
||||
// element has id box-container.
|
||||
'#ajax' => [
|
||||
'callback' => '::promptCallback',
|
||||
'wrapper' => 'box-container',
|
||||
],
|
||||
'#value' => $this->t('Submit'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for submit_driven example.
|
||||
*
|
||||
* Select the 'box' element, change the markup in it, and return it as a
|
||||
* renderable array.
|
||||
*
|
||||
* @return array
|
||||
* Renderable array (the box element)
|
||||
*/
|
||||
public function promptCallback(array &$form, FormStateInterface $form_state) {
|
||||
// In most cases, it is recommended that you put this logic in form
|
||||
// generation rather than the callback. Submit driven forms are an
|
||||
// exception, because you may not want to return the form at all.
|
||||
$element = $form['container'];
|
||||
$element['box']['#markup'] = "Clicked submit ({$form_state->getValue('op')}): " . date('c');
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\ajax_example\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\HtmlCommand;
|
||||
|
||||
/**
|
||||
* AJAX example wizard.
|
||||
*/
|
||||
class Wizard extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'ajax_example_wizard';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $no_js_use = FALSE) {
|
||||
$url = Url::fromUri('internal:/examples/ajax-example/wizard-nojs');
|
||||
$link = Link::fromTextAndUrl($this->t('examples/ajax-example/wizard-nojs'), $url)
|
||||
->toString();
|
||||
|
||||
// Prepare link for multiple arguments.
|
||||
$urltwo = Url::fromUri('internal:/examples/ajax-example/wizard');
|
||||
$linktwo = Link::fromTextAndUrl($this->t('examples/ajax-example/wizard'), $urltwo)
|
||||
->toString();
|
||||
|
||||
// We want to deal with hierarchical form values.
|
||||
$form['#tree'] = TRUE;
|
||||
$form['description'] = [
|
||||
'#markup' => t('This example is a step-by-step wizard. The @link does it without page reloads; the @link1 is the same code but simulates a non-javascript environment, showing it with page reloads.', [
|
||||
'@link' => $linktwo,
|
||||
'@link1' => $link,
|
||||
]),
|
||||
];
|
||||
|
||||
$form['step'] = [
|
||||
'#type' => 'hidden',
|
||||
'#value' => !empty($form_state->getValue('step')) ? $form_state->getValue('step') : 1,
|
||||
];
|
||||
print_r($form_state->getValue('step'));
|
||||
|
||||
if ($form['step']['#value'] == 1) {
|
||||
$form['step1'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Step 1: Personal details'),
|
||||
];
|
||||
$form['step1']['name'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Your name'),
|
||||
'#default_value' => empty($form_state->getValue([
|
||||
'step1',
|
||||
'name',
|
||||
]) ? '' : $form_state->getValue(['step1', 'name'])),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
$form['next'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Next step'),
|
||||
'#ajax' => [
|
||||
'wrapper' => 'ajax-example-wizard',
|
||||
'callback' => '::prompt',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// This simply allows us to demonstrate no-javascript use without
|
||||
// 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.
|
||||
// For demonstration only! You don't need this.
|
||||
if ($no_js_use) {
|
||||
// Remove the #ajax from the above, so ajax.js won't be loaded.
|
||||
// For demonstration only.
|
||||
unset($form['next']['#ajax']);
|
||||
unset($form['prev']['#ajax']);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wizard callback function.
|
||||
*
|
||||
* @param array $form
|
||||
* Form API form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* Form API form.
|
||||
*
|
||||
* @return array
|
||||
* Form array.
|
||||
*/
|
||||
public function prompt(array $form, FormStateInterface $form_state) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save away the current information.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
if ($form_state->getTriggeringElement()['#value'] == $this->t('Submit your information')) {
|
||||
$value_message = $this->t('Your information has been submitted:') . ' ';
|
||||
foreach ($form_state->getValue('value') as $step => $values) {
|
||||
$value_message .= "$step: ";
|
||||
foreach ($values as $key => $value) {
|
||||
$value_message .= "$key=$value, ";
|
||||
}
|
||||
}
|
||||
drupal_set_message($value_message);
|
||||
$form_state->setRebuild(FALSE);
|
||||
// Redirect to #action, else return.
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$step = $form_state->getValue('step');
|
||||
// Increment or decrement the step as needed. Recover values if they
|
||||
// exist.
|
||||
if ($form_state->getTriggeringElement()['#value']->__toString() == $this->t('Next step')) {
|
||||
$step++;
|
||||
}
|
||||
elseif ($form_state->getTriggeringElement()['#value']->__toString() == $this->t('Previous step')) {
|
||||
$step--;
|
||||
}
|
||||
|
||||
switch ($step) {
|
||||
case 1:
|
||||
$form['step1'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Step 1: Personal details'),
|
||||
];
|
||||
$form['step1']['name'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Your name'),
|
||||
'#default_value' => empty($form_state->getValue([
|
||||
'step1',
|
||||
'name',
|
||||
]) ? '' : $form_state->getValue(['step1', 'name'])),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form_state->setValue('step', 1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
unset($form['step1']);
|
||||
unset($form['next']);
|
||||
$form['step2'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Step 2: Street address info'),
|
||||
];
|
||||
$form['step2']['address'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Your street address'),
|
||||
'#default_value' => empty($form_state->getValue([
|
||||
'step2',
|
||||
'address',
|
||||
]) ? '' : $form_state->getValue(['step2', 'address'])),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form_state->setValue('step', $step);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
|
||||
$form['step3'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Step 3: City info'),
|
||||
];
|
||||
$form['step3']['city'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Your city'),
|
||||
'#default_value' => empty($form_state->getValue([
|
||||
'step3',
|
||||
'city',
|
||||
]) ? '' : $form_state->getValue(['step3', 'city'])),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form_state->setValue('step', $step);
|
||||
break;
|
||||
}
|
||||
if ($step == 3) {
|
||||
|
||||
$form['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t("Submit your information"),
|
||||
];
|
||||
}
|
||||
if ($step > 1 && !isset($form['prev'])) {
|
||||
$form['prev'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => t("Previous step"),
|
||||
// Since all info will be discarded, don't validate on 'prev'.
|
||||
'#limit_validation_errors' => [],
|
||||
// #submit is required to use #limit_validation_errors.
|
||||
'#submit' => ['ajax_example_wizard_submit'],
|
||||
'#ajax' => [
|
||||
'wrapper' => 'ajax-example-wizard',
|
||||
'callback' => '::prompt',
|
||||
],
|
||||
];
|
||||
}
|
||||
if ($step < 3 && !isset($form['next'])) {
|
||||
$form['next'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Next step'),
|
||||
'#limit_validation_errors' => [],
|
||||
'#ajax' => [
|
||||
'wrapper' => 'ajax-example-wizard',
|
||||
'callback' => '::prompt',
|
||||
],
|
||||
];
|
||||
}
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new HtmlCommand('#ajax-example-wizard', $form));
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{#
|
||||
|
||||
Description text for the Ajax Example.
|
||||
|
||||
#}
|
||||
|
||||
{% set simple_ajax_example = path('ajax_example.simplest') %}
|
||||
{% set ajax_generate_textfields = path('ajax_example.autotextfields') %}
|
||||
{% set ajax_submit = path('ajax_example.submit_driven_ajax') %}
|
||||
{% set ajax_dependent_dropdown = path('ajax_example.dependent_dropdown') %}
|
||||
{% set ajax_dependent_dropdown_nojs = path('ajax_example.dependent_dropdown', {'nojs': 'nojs'}) %}
|
||||
{% set ajax_dynamic_form = path('ajax_example.dynamic_form_sections') %}
|
||||
{% set ajax_dynamic_form_nojs = path('ajax_example.dynamic_form_sections', {'nojs': 'nojs'}) %}
|
||||
{% set ajax_wizard_example = path('ajax_example.wizard') %}
|
||||
{% set ajax_wizard_example_nojs = path('ajax_example.wizardnojs') %}
|
||||
|
||||
{% trans %}
|
||||
|
||||
<p>The AJAX example module provides many examples of AJAX including forms, links, and AJAX commands.</p>
|
||||
<p><a href={{ simple_ajax_example }}>Simplest AJAX Example</a></p>
|
||||
<p><a href={{ ajax_generate_textfields }}>Generate textfields</a></p>
|
||||
<p><a href={{ ajax_submit }}>Submit-driven AJAX</a></p>
|
||||
<p><a href={{ ajax_dependent_dropdown }}>Dependent dropdown</a></p>
|
||||
<p><a href={{ ajax_dependent_dropdown_nojs }}>Dependent dropdown w/ no JS</a></p>
|
||||
<p><a href={{ ajax_dynamic_form }}>Dynamic form sections</a></p>
|
||||
<p><a href={{ ajax_dynamic_form_nojs }}>Dynamic form sections w/ no JS</a></p>
|
||||
<p><a href={{ ajax_wizard_example }}>AJAX Wizard Example</a></p>
|
||||
<p><a href={{ ajax_wizard_example_nojs }}>AJAX Wizard Example w/JS turned off</a></p>
|
||||
{% endtrans %}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Verify tool block menu items and operability of all our routes.
|
||||
*
|
||||
* @group ajax_example
|
||||
* @group examples
|
||||
*
|
||||
* @ingroup ajax_example
|
||||
*/
|
||||
class AjaxExampleMenuTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* The installation profile to use with this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'minimal';
|
||||
|
||||
/**
|
||||
* Tests links.
|
||||
*/
|
||||
public function testAjaxExampleLinks() {
|
||||
// Login a user that can access content.
|
||||
$this->drupalLogin(
|
||||
$this->createUser(['access content', 'access user profiles'])
|
||||
);
|
||||
|
||||
$assertion = $this->assertSession();
|
||||
|
||||
// Routes with menu links, and their form buttons.
|
||||
$routes_with_menu_links = [
|
||||
'ajax_example.description' => [],
|
||||
'ajax_example.simplest' => [],
|
||||
'ajax_example.autotextfields' => ['Click Me'],
|
||||
'ajax_example.submit_driven_ajax' => ['Submit'],
|
||||
'ajax_example.dependent_dropdown' => ['Submit'],
|
||||
'ajax_example.dynamic_form_sections' => ['Choose'],
|
||||
'ajax_example.wizard' => ['Next step'],
|
||||
'ajax_example.wizardnojs' => ['Next step'],
|
||||
'ajax_example.ajax_link_render' => [],
|
||||
'ajax_example.autocomplete_user' => ['Submit'],
|
||||
];
|
||||
|
||||
// Ensure the links appear in the tools menu sidebar.
|
||||
$this->drupalGet('');
|
||||
foreach (array_keys($routes_with_menu_links) as $route) {
|
||||
$assertion->linkByHrefExists(Url::fromRoute($route)->getInternalPath());
|
||||
}
|
||||
|
||||
// All our routes with their form buttons.
|
||||
$routes = [
|
||||
'ajax_example.ajax_link_callback' => [],
|
||||
];
|
||||
|
||||
// Go to all the routes and click all the buttons.
|
||||
$routes = array_merge($routes_with_menu_links, $routes);
|
||||
foreach ($routes as $route => $buttons) {
|
||||
$url = Url::fromRoute($route);
|
||||
$this->drupalGet($url);
|
||||
$assertion->statusCodeEquals(200);
|
||||
foreach ($buttons as $button) {
|
||||
$this->drupalPostForm($url, [], $button);
|
||||
$assertion->statusCodeEquals(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Functional test of non-AJAX dependent dropdown example.
|
||||
*
|
||||
* @group ajax_example
|
||||
* @group examples
|
||||
*
|
||||
* @ingroup ajax_example
|
||||
*
|
||||
* @see \Drupal\Tests\ajax_example\FunctionalJavascript\DependentDropdownTest
|
||||
*/
|
||||
class DependentDropdownTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* Test the dependent dropdown form without AJAX.
|
||||
*/
|
||||
public function testDependentDropdown() {
|
||||
// Get the Mink stuff.
|
||||
$session = $this->getSession();
|
||||
$assert = $this->assertSession();
|
||||
$page = $session->getPage();
|
||||
|
||||
// Get a URL object for the form, specifying no JS.
|
||||
$dropdown_url = Url::fromRoute('ajax_example.dependent_dropdown', ['nojs' => 'nojs']);
|
||||
|
||||
// Get the form.
|
||||
$this->drupalGet($dropdown_url);
|
||||
// Check for the initial state.
|
||||
$assert->fieldDisabled('instrument_dropdown');
|
||||
$assert->fieldValueEquals('instrument_dropdown', 'none');
|
||||
$submit_button = $page->findButton('edit-submit');
|
||||
$this->assertTrue($submit_button->hasAttribute('disabled'));
|
||||
|
||||
// Run through the matrix of form submissions.
|
||||
$families = [
|
||||
'String' => ['Violin', 'Viola', 'Cello', 'Double Bass'],
|
||||
'Woodwind' => ['Flute', 'Clarinet', 'Oboe', 'Bassoon'],
|
||||
'Brass' => ['Trumpet', 'Trombone', 'French Horn', 'Euphonium'],
|
||||
'Percussion' => ['Bass Drum', 'Timpani', 'Snare Drum', 'Tambourine'],
|
||||
];
|
||||
|
||||
foreach ($families as $family => $instruments) {
|
||||
// Post the form for the instrument family.
|
||||
$this->drupalPostForm($dropdown_url, ['instrument_family_dropdown' => $family], 'Choose');
|
||||
// Get the instrument dropdown elements.
|
||||
$instrument_options = $page->findAll('css', '#edit-instrument-dropdown option');
|
||||
$this->assertCount(count($instruments), $instrument_options);
|
||||
// Make sure all the instruments are in the select dropdown.
|
||||
foreach ($instrument_options as $instrument) {
|
||||
$this->assertContains($instrument->getAttribute('value'), $instruments);
|
||||
}
|
||||
// Post each instrument. We have to 'choose' again in order to unlock the
|
||||
// instrument dropdown.
|
||||
foreach ($instruments as $instrument) {
|
||||
$this->drupalPostForm($dropdown_url, ['instrument_family_dropdown' => $family], 'Choose');
|
||||
$this->drupalPostForm(NULL, ['instrument_dropdown' => $instrument], 'Submit');
|
||||
$assert->pageTextContains("Your values have been submitted. Instrument family: $family, Instrument: $instrument");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Functional test of non-AJAX dependent dropdown example.
|
||||
*
|
||||
* @group ajax_example
|
||||
* @group examples
|
||||
*
|
||||
* @ingroup ajax_example
|
||||
*
|
||||
* @see Drupal\Tests\ajax_example\FunctionalJavascript\DynamicFormSectionsTest
|
||||
*/
|
||||
class DynamicFormSectionsTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* Test the dynamic sections form without AJAX.
|
||||
*/
|
||||
public function testDynamicFormSections() {
|
||||
// Get the Mink stuff.
|
||||
$assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Get a URL object for the form, specifying no JS.
|
||||
$dropdown_url = Url::fromRoute('ajax_example.dynamic_form_sections', ['nojs' => 'nojs']);
|
||||
|
||||
// Get the form.
|
||||
$this->drupalGet($dropdown_url);
|
||||
// Check for the initial state.
|
||||
$detail_children = $page->findAll('css', 'div.details-wrapper *');
|
||||
$this->assertEmpty($detail_children);
|
||||
|
||||
// Go through the dropdown options. First outlier is 'Choose question style'
|
||||
// which should have an empty details section.
|
||||
$this->drupalPostForm($dropdown_url, ['question_type_select' => 'Choose question style'], 'Choose');
|
||||
$detail_children = $page->findAll('css', 'div.details-wrapper *');
|
||||
$this->assertEmpty($detail_children);
|
||||
|
||||
// Cycle through the other dropdown values.
|
||||
$question_styles = [
|
||||
'Multiple Choice',
|
||||
'True/False',
|
||||
'Fill-in-the-blanks',
|
||||
];
|
||||
// These all add stuff to the details wrapper.
|
||||
foreach ($question_styles as $question_style) {
|
||||
$this->drupalPostForm($dropdown_url, ['question_type_select' => $question_style], 'Choose');
|
||||
$detail_children = $page->findAll('css', 'div.details-wrapper *');
|
||||
$this->assertNotEmpty($detail_children);
|
||||
$this->drupalPostForm(NULL, ['question' => 'George Washington'], 'Submit your answer');
|
||||
$assert->pageTextContains('You got the right answer: George Washington');
|
||||
}
|
||||
// One wrong answer to exercise that code path.
|
||||
$this->drupalPostForm($dropdown_url, ['question_type_select' => 'Multiple Choice'], 'Choose');
|
||||
$detail_children = $page->findAll('css', 'div.details-wrapper *');
|
||||
$this->assertNotEmpty($detail_children);
|
||||
$this->drupalPostForm(NULL, ['question' => 'Abraham Lincoln'], 'Submit your answer');
|
||||
$assert->pageTextContains('Sorry, your answer (Abraham Lincoln) is wrong');
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Test the user interactions for the Autotextfields example.
|
||||
*
|
||||
* @group ajax_example
|
||||
*/
|
||||
class AutofieldsTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* Test the user interactions for the Autotextfields example.
|
||||
*/
|
||||
public function testAutotextfields() {
|
||||
// Get our Mink stuff.
|
||||
$session = $this->getSession();
|
||||
$page = $session->getPage();
|
||||
$assert = $this->assertSession();
|
||||
|
||||
// Get the page.
|
||||
$form_url = Url::fromRoute('ajax_example.autotextfields');
|
||||
$this->drupalGet($form_url);
|
||||
|
||||
// Check our initial state.
|
||||
$assert->checkboxNotChecked('ask_first_name');
|
||||
$assert->checkboxNotChecked('ask_last_name');
|
||||
$assert->fieldNotExists('first_name');
|
||||
$assert->fieldNotExists('last_name');
|
||||
// Submit the form. This tests what happens when there are no user
|
||||
// interactions because drupalPostForm() reloads the form.
|
||||
$this->drupalPostForm($form_url, [], 'Click Me');
|
||||
$assert->pageTextContains('Submit handler: First name: n/a Last name: n/a');
|
||||
|
||||
// Ask for the first name.
|
||||
$page->checkField('ask_first_name');
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
$assert->fieldExists('first_name');
|
||||
$assert->fieldNotExists('last_name');
|
||||
// Submit the form. We have to find the field and set its value rather than
|
||||
// use drupalPostForm(), because when we post the form, it will be rebuilt.
|
||||
// We are testing the form state after AJAX has modified it, so we must
|
||||
// preserve that.
|
||||
$page->findField('first_name')->setValue('Dries');
|
||||
$page->pressButton('Click Me');
|
||||
$assert->pageTextContains('Submit handler: First name: Dries Last name: n/a');
|
||||
|
||||
// Ask for the first and last name.
|
||||
$page->checkField('ask_first_name');
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
$assert->fieldExists('first_name');
|
||||
$page->checkField('ask_last_name');
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
$assert->fieldExists('last_name');
|
||||
// Submit the form.
|
||||
$page->findField('first_name')->setValue('Dries');
|
||||
$page->findField('last_name')->setValue('Buytaert');
|
||||
$page->pressButton('Click Me');
|
||||
$assert->pageTextContains('Submit handler: First name: Dries Last name: Buytaert');
|
||||
|
||||
// Ask for only the last name.
|
||||
$page->checkField('ask_last_name');
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
$assert->fieldNotExists('first_name');
|
||||
$assert->fieldExists('last_name');
|
||||
// Submit the form.
|
||||
$page->findField('last_name')->setValue('Buytaert');
|
||||
$page->pressButton('Click Me');
|
||||
$assert->pageTextContains('Submit handler: First name: n/a Last name: Buytaert');
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Functional test of dependent dropdown example.
|
||||
*
|
||||
* @group ajax_example
|
||||
*
|
||||
* @see \Drupal\Tests\ajax_example\FunctionalJavascript\DependentDropdownTest
|
||||
*/
|
||||
class DependentDropdownTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* Test the dependent dropdown form with AJAX.
|
||||
*/
|
||||
public function testDependentDropdown() {
|
||||
// Get the Mink stuff.
|
||||
$assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Get a URL object for the form, specifying AJAX.
|
||||
$dropdown_url = Url::fromRoute('ajax_example.dependent_dropdown', ['nojs' => 'ajax']);
|
||||
|
||||
// Get the form.
|
||||
$this->drupalGet($dropdown_url);
|
||||
// Check for the initial state.
|
||||
$assert->fieldDisabled('instrument_dropdown');
|
||||
$assert->fieldValueEquals('instrument_dropdown', 'none');
|
||||
$submit_button = $page->findButton('edit-submit');
|
||||
$this->assertTrue($submit_button->hasAttribute('disabled'));
|
||||
|
||||
// Run through the matrix of families.
|
||||
$families = [
|
||||
'String' => ['Violin', 'Viola', 'Cello', 'Double Bass'],
|
||||
'Woodwind' => ['Flute', 'Clarinet', 'Oboe', 'Bassoon'],
|
||||
'Brass' => ['Trumpet', 'Trombone', 'French Horn', 'Euphonium'],
|
||||
'Percussion' => ['Bass Drum', 'Timpani', 'Snare Drum', 'Tambourine'],
|
||||
];
|
||||
|
||||
foreach ($families as $family => $instruments) {
|
||||
// Select a family.
|
||||
$family_dropdown = $assert->fieldExists('instrument_family_dropdown');
|
||||
$family_dropdown->setValue($family);
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
|
||||
// Get the instrument dropdown elements.
|
||||
$instrument_options = $page->findAll('css', 'select[name="instrument_dropdown"] option');
|
||||
$this->assertCount(count($instruments), $instrument_options);
|
||||
// Make sure all the instruments are in the select dropdown.
|
||||
foreach ($instrument_options as $instrument) {
|
||||
$this->assertContains($instrument->getAttribute('value'), $instruments);
|
||||
}
|
||||
|
||||
// Post each instrument.
|
||||
foreach ($instruments as $instrument) {
|
||||
$this->drupalGet($dropdown_url);
|
||||
$family_dropdown->setValue($family);
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
$this->drupalPostForm(NULL, ['instrument_dropdown' => $instrument], 'Submit');
|
||||
$assert->pageTextContains("Your values have been submitted. Instrument family: $family, Instrument: $instrument");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Functional test of dependent dropdown example.
|
||||
*
|
||||
* @group ajax_example
|
||||
*
|
||||
* @see Drupal\Tests\ajax_example\Functional\DynamicFormSectionsTest
|
||||
*/
|
||||
class DynamicFormSectionsTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* Test the dependent dropdown form with AJAX.
|
||||
*/
|
||||
public function testDynamicFormSections() {
|
||||
// Get the Mink stuff.
|
||||
$assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Get a URL object for the form, specifying no JS.
|
||||
$dropdown_url = Url::fromRoute('ajax_example.dynamic_form_sections', ['nojs' => 'ajax']);
|
||||
|
||||
// Get the form.
|
||||
$this->drupalGet($dropdown_url);
|
||||
// Check for the initial state.
|
||||
$this->assertEmpty($page->findAll('css', 'div.details-wrapper *'));
|
||||
|
||||
// Cycle through the other dropdown values.
|
||||
$question_styles = [
|
||||
'Multiple Choice',
|
||||
'True/False',
|
||||
'Fill-in-the-blanks',
|
||||
];
|
||||
|
||||
// Check expectations against the details wrapper.
|
||||
$question_type_dropdown = $page->findField('question_type_select');
|
||||
foreach ($question_styles as $question_style) {
|
||||
$question_type_dropdown->setValue($question_style);
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
$this->assertNotEmpty($page->findAll('css', 'div.details-wrapper *'));
|
||||
}
|
||||
// Prompt to choose question should remove the question.
|
||||
$question_type_dropdown->setValue('Choose question style');
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
$this->assertEmpty($page->findAll('css', 'div.details-wrapper *'));
|
||||
|
||||
// Submit the correct answers.
|
||||
foreach ($question_styles as $question_style) {
|
||||
$this->drupalGet($dropdown_url);
|
||||
$question_type_dropdown->setValue($question_style);
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
$this->drupalPostForm(NULL, ['question' => 'George Washington'], 'Submit your answer');
|
||||
$assert->pageTextContains('You got the right answer: George Washington');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Tests the behavior of the entity_autocomplete example.
|
||||
*
|
||||
* @group ajax_example
|
||||
*/
|
||||
class EntityAutocompleteTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* Test the behavior of the submit-driven AJAX example.
|
||||
*
|
||||
* Behaviors to test:
|
||||
* - GET the route ajax_example.autocomplete_user.
|
||||
* - Examine the DOM to make sure our change hasn't happened yet.
|
||||
* - Send an event to the DOM to trigger the autocomplete.
|
||||
* - Wait for the autocomplete request to complete.
|
||||
* - Examine the DOM to see if our expected change happened.
|
||||
* - Submit some names to see if our form processed the user properly.
|
||||
*/
|
||||
public function testSubmitDriven() {
|
||||
// Set up some accounts with known names.
|
||||
$names = ['bb', 'bc'];
|
||||
foreach ($names as $name) {
|
||||
$this->createUser([], $name);
|
||||
}
|
||||
|
||||
// Get our various Mink elements.
|
||||
$assert = $this->assertSession();
|
||||
$session = $this->getSession();
|
||||
$page = $session->getPage();
|
||||
// We'll be using the users field quite a bit, so let's make it a variable.
|
||||
$users_field_name = 'edit-users';
|
||||
|
||||
// Get the form.
|
||||
$this->drupalGet(Url::fromRoute('ajax_example.autocomplete_user'));
|
||||
// Examine the DOM to make sure our change hasn't happened yet.
|
||||
$assert->fieldValueEquals($users_field_name, '');
|
||||
|
||||
// Send an event to the DOM. This will start the autocomplete process.
|
||||
$autocomplete_field = $page->findField($users_field_name);
|
||||
$session->getDriver()->keyDown($autocomplete_field->getXpath(), 'b');
|
||||
|
||||
// Wait for the autocomplete request to complete.
|
||||
$assert->waitOnAutocomplete();
|
||||
|
||||
// Examine the DOM to see if our expected change happened.
|
||||
$results = $page->findAll('css', '.ui-autocomplete li');
|
||||
$this->assertCount(2, $results);
|
||||
foreach ($results as $result) {
|
||||
$this->assertContains($result->getText(), $names);
|
||||
}
|
||||
|
||||
// Submit to see if our form processed the user properly.
|
||||
$this->submitForm([$users_field_name => 'bb, bc'], 'Submit');
|
||||
$assert->pageTextContains('These are your users: bb bc');
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Test the user interactions for the Simplest example.
|
||||
*
|
||||
* @group ajax_example
|
||||
*/
|
||||
class SimplestTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* Test the user interactions for the Autotextfields example.
|
||||
*/
|
||||
public function testAutotextfields() {
|
||||
// Get our Mink stuff.
|
||||
$session = $this->getSession();
|
||||
$page = $session->getPage();
|
||||
$assert = $this->assertSession();
|
||||
|
||||
// Get the page.
|
||||
$form_url = Url::fromRoute('ajax_example.simplest');
|
||||
$this->drupalGet($form_url);
|
||||
|
||||
// Don't repeat ourselves. This makes it easier if we change the markup
|
||||
// later.
|
||||
$description_selector = '#replace-textfield-container div.description';
|
||||
|
||||
// Check our initial state.
|
||||
$assert->elementExists('css', '#replace-textfield-container');
|
||||
$assert->elementNotExists('css', $description_selector);
|
||||
|
||||
// Select values on the dropdown. Start with three so the change event is
|
||||
// triggered.
|
||||
foreach (['three', 'two', 'one'] as $value) {
|
||||
// Select the dropdown value.
|
||||
$page->selectFieldOption('changethis', $value);
|
||||
// Wait for AJAX to happen.
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
// Assert that the description exists.
|
||||
$assert->elementExists('css', $description_selector);
|
||||
// Get the description element from the page.
|
||||
$prompt_element = $page->find('css', $description_selector);
|
||||
// Assert that the description element says what we expect it to say.
|
||||
$this->assertEquals(
|
||||
"Say why you chose '$value'",
|
||||
$prompt_element->getText()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\ajax_example\FunctionalJavascript;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
|
||||
/**
|
||||
* Tests the behavior of the submit-driven AJAX example.
|
||||
*
|
||||
* @group ajax_example
|
||||
*/
|
||||
class SubmitDrivenTest extends JavascriptTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ajax_example'];
|
||||
|
||||
/**
|
||||
* Test the behavior of the submit-driven AJAX example.
|
||||
*
|
||||
* Behaviors to test:
|
||||
* - GET the route ajax_example.submit_driven_ajax.
|
||||
* - Examine the DOM to make sure our change hasn't happened yet.
|
||||
* - Submit the form.
|
||||
* - Wait for the AJAX request to complete.
|
||||
* - Examine the DOM to see if our expected change happened.
|
||||
*/
|
||||
public function testSubmitDriven() {
|
||||
// Get the session assertion object.
|
||||
$assert = $this->assertSession();
|
||||
// Get the page.
|
||||
$this->drupalGet(Url::fromRoute('ajax_example.submit_driven_ajax'));
|
||||
// Examine the DOM to make sure our change hasn't happened yet.
|
||||
$assert->pageTextNotContains('Clicked submit (Submit):');
|
||||
// Submit the form.
|
||||
$this->submitForm([], 'Submit');
|
||||
// Wait on the AJAX request.
|
||||
$assert->assertWaitOnAjaxRequest();
|
||||
// Compare DOM to our expectations.
|
||||
$assert->pageTextContains('Clicked submit (Submit):');
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user