diff --git a/ajax_example/ajax_example.info.yml b/ajax_example/ajax_example.info.yml
new file mode 100644
index 0000000..b02c330
--- /dev/null
+++ b/ajax_example/ajax_example.info.yml
@@ -0,0 +1,7 @@
+name: 'AJAX Example'
+type: module
+description: 'An example module showing how to use Drupal AJAX forms.'
+package: 'Example modules'
+core: 8.x
+dependencies:
+  - drupal:examples
diff --git a/ajax_example/ajax_example.install b/ajax_example/ajax_example.install
new file mode 100644
index 0000000..033516c
--- /dev/null
+++ b/ajax_example/ajax_example.install
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * AJAX Examples install file schema for ajax_example_form_node_form_alter()
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function ajax_example_schema() {
+  $schema['ajax_example_node_form_alter'] = [
+    'description' => 'Stores example settings for nodes.',
+    'fields' => [
+      'nid' => [
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The {node}.nid to store settings.',
+      ],
+      'example_1' => [
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Node Form Example 1 checkbox',
+      ],
+      'example_2' => [
+        'type' => 'varchar',
+        'length' => 256,
+        'not null' => FALSE,
+        'default' => '',
+        'description' => 'Node Form Example 2 textfield',
+      ],
+    ],
+    'primary key' => ['nid'],
+    'foreign keys' => [
+      'dnv_node' => [
+        'table' => 'node',
+        'columns' => ['nid' => 'nid'],
+      ],
+    ],
+  ];
+  return $schema;
+}
+
+/**
+ * Add the new ajax_example_node_form_alter table.
+ */
+function ajax_example_update_7100() {
+  if (!db_table_exists('ajax_example_node_form_alter')) {
+    $schema = ajax_example_schema();
+    db_create_table('ajax_example_node_form_alter', $schema['ajax_example_node_form_alter']);
+    return st('Created table ajax_example_node_form_alter');
+  }
+}
diff --git a/ajax_example/ajax_example.libaries.yml b/ajax_example/ajax_example.libaries.yml
new file mode 100644
index 0000000..ecfeaf1
--- /dev/null
+++ b/ajax_example/ajax_example.libaries.yml
@@ -0,0 +1,7 @@
+ajax_example.dropdown:
+  version: VERSION
+  css:
+    theme:
+      css/ajax_example.css: {}
+  js:
+    js/ajax_example.js: {}
diff --git a/ajax_example/ajax_example.links.menu.yml b/ajax_example/ajax_example.links.menu.yml
new file mode 100644
index 0000000..2e50105
--- /dev/null
+++ b/ajax_example/ajax_example.links.menu.yml
@@ -0,0 +1,73 @@
+ajax_example.description:
+  title: 'AJAX Example'
+  route_name: 'ajax_example.description'
+   weight: 10
+ajax_example.simplest:
+  title: 'Simplest AJAX Example'
+  route_name: 'ajax_example.simplest'
+  parent: ajax_example.description
+
+ajax_example.autocheckboxes:
+  title: 'Generate checkboxes'
+  route_name: 'ajax_example.autocheckboxes'
+  parent: ajax_example.description
+
+ajax_example.autotextfields:
+  title: 'Generate textfields'
+  route_name: 'ajax_example.autotextfields'
+  parent: ajax_example.description
+
+ajax_example.submit-driven:
+  title: 'Submit-driven AJAX'
+  route_name: 'ajax_example.submit-driven-ajax'
+  parent: ajax_example.description
+
+ajax_example.dependent-dropdown:
+  title: 'Dependent dropdown'
+  route_name: 'ajax_example.dependent_dropdown'
+  parent: ajax_example.description
+
+ajax_example.dependent-dropdown-degardes:
+  title: 'Dependent dropdown degardes'
+  route_name: 'ajax_example.dependent_dropdown_degardes'
+  parent: ajax_example.description
+
+ajax_example.dependent-dropdown-degardes-nojava:
+  title: 'Dependent dropdown degardes w/JS turned off '
+  route_name: 'ajax_example.dependent_dropdown_degardes_nojava'
+  parent: ajax_example.description
+
+ajax_example.dynamic-dropdown:
+  title: 'Dynamic Sections (with graceful degradation)'
+  route_name: 'ajax_example.dynamic_sections'
+  parent: ajax_example.description
+
+ajax_example.dynamic-dropdown-degardes-nojava-example:
+  title: 'Dynamic Sections (with graceful degradation) w/js turned off'
+  route_name: 'ajax_example.dynamic_sections_nojava'
+  parent: ajax_example.description
+
+ajax_example.wizard-example:
+  title: 'AJAX Wizard Example'
+  route_name: 'ajax_example.wizard'
+  parent: ajax_example.description
+
+ajax_example.wizard-examplenojs:
+  title: 'AJAX Wizard Example w/JS turned off'
+  route_name: 'ajax_example.wizardnojs'
+  parent: ajax_example.description
+
+ajax_example.addmorenojs:
+  title: 'Add more button (with graceful degradation) w/JS turned off'
+  route_name: 'ajax_example.addmorenojs'
+  parent: ajax_example.description
+
+ajax_example.addmore:
+  title: 'AJAX Add more button'
+  route_name: 'ajax_example.addmore'
+  parent: ajax_example.description
+
+ajax_example.advanced:
+  title: 'AJAX framework commands'
+  route_name: 'ajax_example.advanced'
+  parent: ajax_example.description
diff --git a/ajax_example/ajax_example.module b/ajax_example/ajax_example.module
new file mode 100644
index 0000000..11a7453
--- /dev/null
+++ b/ajax_example/ajax_example.module
@@ -0,0 +1,108 @@
+<?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".
+ */
+
+/**
+ * Helper function to populate the first dropdown.
+ *
+ * This would normally be pulling data from the database.
+ *
+ * @return array
+ *   Dropdown options.
+ */
+function _ajax_example_get_first_dropdown_options() {
+  return [
+    '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
+ */
+function _ajax_example_get_second_dropdown_options($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;
+}
+
+/**
+ * Submit function for AjaxExampleDependentDropdownDegrades::prompt().
+ */
+
+/**
+ * Submit function for ajax_example_wizard.
+ *
+ * In AJAX this is only submitted when the final submit button is clicked,
+ * but in the non-javascript situation, it is submitted with every
+ * button click.
+ */
diff --git a/ajax_example/ajax_example.routing.yml b/ajax_example/ajax_example.routing.yml
new file mode 100644
index 0000000..39c12bb
--- /dev/null
+++ b/ajax_example/ajax_example.routing.yml
@@ -0,0 +1,194 @@
+ajax_example.description:
+  path: 'examples/ajax-example'
+  defaults:
+    _controller: '\Drupal\ajax_example\Controller\AjaxExampleController::description'
+    _title: 'AJAX Example'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.simplest:
+  path: 'examples/ajax-example/simplest'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleSimplest'
+    _title: 'Simplest AJAX Example'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.progresssbar:
+  path: 'examples/ajax-example/progressbar'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleProgressBar'
+    _title: 'Progress bar'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.progresssbarProgress:
+  path: 'examples/ajax_example/progressbar/progress/{time}'
+  defaults:
+    _controller: '\Drupal\ajax_example\Controller\AjaxExampleController::progressbarProgress'
+    _title: 'Progress bar Progress'
+     # We provide default value to both arguments.
+    time: ''
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.autocheckboxes:
+  path: 'examples/ajax-example/autocheckboxes'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleAutocheckboxes'
+    _title: 'Generate checkboxes'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.autotextfields:
+  path: 'examples/ajax-example/autotextfields'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleAutotextfields'
+    _title: 'Generate textfields'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.submit-driven-ajax:
+  path: 'examples/ajax-example/submit-driven-ajax'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleSubmitDriven'
+    _title: 'Submit-driven AJAX'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.dependent_dropdown:
+  path: 'examples/ajax-example/dependent-dropdown'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleDependentDropdown'
+    _title: 'Dependent dropdown'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.dependent_dropdown_degardes:
+  path: 'examples/ajax-example/dependent-dropdown-degardes'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleDependentDropdownDegardes'
+    _title: 'Dependent dropdown degardes'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.dependent_dropdown_degardes_nojava:
+  path: 'examples/ajax-example/dependent-dropdown-degardes-nojava/{no_js_use}'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleDependentDropdownDegardes'
+    _title: 'Dependent dropdown degardes w/JS turned off '
+    no_js_use: TRUE
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.dynamic_sections:
+  path: 'examples/ajax-example/dynamic-sections'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleDynamicSectionsDegardes'
+    _title: 'Dynamic Sections (with graceful degradation)'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.dynamic_sections_nojava:
+  path: 'examples/ajax-example/dynamic-sections-nojava/{no_js_use}'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleDynamicSectionsDegardes'
+    _title: 'Dynamic Sections w/JS turned off'
+    no_js_use: TRUE
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.wizard:
+  path: 'examples/ajax-example/wizard'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleWizard'
+    _title: 'Wizard (with graceful degradation)'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.wizardnojs:
+  path: 'examples/ajax-example/wizard-nojs/{no_js_use}'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleWizard'
+    _title: 'Wizard (with graceful degradation) w/JS turned off'
+    no_js_use: TRUE
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.addmore:
+  path: 'examples/ajax-example/add-more'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleAddMore'
+    _title: 'Add more button (with graceful degradation)'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.addmorenojs:
+  path: 'examples/ajax-example/add-more-nojs/{no_js_use}'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleAddMore'
+    _title: 'Add more button (with graceful degradation) w/JS turned off'
+    no_js_use: TRUE
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.advanced:
+  path: 'examples/ajax-example/advanced-commands'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleAdvancedCommands'
+    _title: 'AJAX framework commands'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.Ajaxlink:
+  path: 'examples/ajax-example/ajax-link'
+  defaults:
+    _controller: '\Drupal\ajax_example\Controller\AjaxExampleController::ajaxExampleRenderLink'
+    _title: 'Ajax Link (Renderable Array)'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.Ajaxlinkrender:
+  path: 'examples/ajax-example/ajax-link-renderable'
+  defaults:
+    _controller: '\Drupal\ajax_example\Controller\AjaxExampleController::ajaxExampleRenderLinkRa'
+    _title: 'Ajax Link (Renderable Array)'
+  requirements:
+    _access: 'TRUE'
+
+ajax_example.Ajaxlinkcall:
+  path: 'ajax_link_callback'
+  defaults:
+    _controller: '\Drupal\ajax_example\Controller\AjaxExampleController::ajaxlinkresponse'
+  requirements:
+    _permission: 'access content'
+
+ajax_example.AutocompleteSimplest:
+  path: 'examples/ajax_example/simple_autocomplete'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleAutocomplete'
+    _title: 'Autocomplete (simple)'
+  requirements:
+    _permission: 'access content, access user profiles'
+
+ajax_example.AutocompleteSimplestcallback:
+  path: 'examples/ajax_example/simple_user_autocomplete_callback'
+  defaults:
+    _controller: '\Drupal\ajax_example\Controller\AjaxExampleController::ajax_example_simple_user_autocomplete_callback'
+  requirements:
+    _permission: 'access user profiles, access content'
+
+ajax_example.Autocompletenode:
+  path: 'examples/ajax_example/node_autocomplete'
+  defaults:
+    _form: '\Drupal\ajax_example\Form\AjaxExampleUniquecomplete'
+    _title: 'Autocomplete (node with nid)'
+  requirements:
+    _permission: 'access content'
+
+ajax_example.Autocompletenodecallback:
+  path: 'examples/ajax_example/unique_node_autocomplete_callback'
+  defaults:
+    _controller: '\Drupal\ajax_example\Controller\AjaxExampleController::ajax_example_unique_node_autocomplete_callback'
+  requirements:
+    _permission: 'access content'
diff --git a/ajax_example/css/ajax_example.css b/ajax_example/css/ajax_example.css
new file mode 100644
index 0000000..1e94a09
--- /dev/null
+++ b/ajax_example/css/ajax_example.css
@@ -0,0 +1,16 @@
+/*
+ * @file
+ * CSS for ajax_example.
+ *
+ * details on what this file does. It is not used in any other example.
+ */
+/* Hides the next button when not degrading to non-javascript browser */
+html.js .next-button {
+  display: none;
+}
+
+/* Makes the next/choose button align to the right of the select control */
+.form-item-dropdown-first,
+.form-item-question-type-select {
+  display: inline-block;
+}
diff --git a/ajax_example/js/ajax_example.js b/ajax_example/js/ajax_example.js
new file mode 100644
index 0000000..feb9559
--- /dev/null
+++ b/ajax_example/js/ajax_example.js
@@ -0,0 +1,26 @@
+/**
+ * @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.
+    if (Drupal.ajax) {
+      $('.enabled-for-ajax').removeAttr('disabled');
+    }
+
+    // Below is only for the demo case of showing with js turned off.
+    // It overrides the behavior of the CSS that would normally turn off
+    // 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();
+    }
+  }
+  };
+
+})(jQuery);
diff --git a/ajax_example/src/Controller/AjaxExampleController.php b/ajax_example/src/Controller/AjaxExampleController.php
new file mode 100644
index 0000000..356d199
--- /dev/null
+++ b/ajax_example/src/Controller/AjaxExampleController.php
@@ -0,0 +1,270 @@
+<?php
+
+namespace Drupal\ajax_example\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\examples\Utility\DescriptionTemplateTrait;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Drupal\Core\Link;
+use Drupal\Core\Url;
+use Drupal\Core\Ajax\AppendCommand;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Database\Connection;
+use Drupal\Component\Utility\SafeMarkup;
+
+/**
+ * Controller routines for AJAX example routes.
+ */
+class AjaxExampleController extends ControllerBase {
+  use DescriptionTemplateTrait;
+  protected $connection;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getModuleName() {
+    return 'ajax_example';
+  }
+
+  /**
+   *
+   */
+  public function basicInstructions() {
+    return [
+      $this->description(),
+    ];
+  }
+
+  /**
+   * Get the progress bar execution status, as JSON.
+   *
+   * This is the menu handler for
+   * examples/ajax_example/progressbar/progress/$time.
+   *
+   * This function is our wholly arbitrary job that we're checking the status for.
+   * In this case, we're reading a system variable that is being updated by
+   * ajax_example_progressbar_callback().
+   *
+   * We set up the AJAX progress bar to check the status every second, so this
+   * will execute about once every second.
+   *
+   * The progress bar JavaScript accepts two values: message and percentage. We
+   * set those in an array and in the end convert it JSON for sending back to the
+   * client-side JavaScript.
+   *
+   * @param int $time
+   *   Timestamp.
+   *
+   * @return \Symfony\Component\HttpFoundation\JsonResponse
+   */
+  public function progressbarProgress($time) {
+    $progress = [
+      'message' => t('Starting execute...'),
+      'percentage' => -1,
+    ];
+
+    $completed_percentage = \Drupal::config('ajaxexample.settings')->get('example_progressbar_' . $time);
+
+    if ($completed_percentage) {
+      $progress['message'] = t('Executing...');
+      $progress['percentage'] = $completed_percentage;
+    }
+
+    return new JsonResponse($progress);
+  }
+
+  /**
+   * 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 ajaxExampleRenderLinkRa() {
+
+    $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.'),
+    ];
+    $build['ajax_link'] = [
+      '#type' => 'link',
+      '#title' => t('Click here'),
+    // Note the /nojs portion of the href - if javascript is enabled,
+    // this part will be stripped from the path before it is called.
+      '#href' => 'ajax_link_callback/nojs/',
+      '#id' => 'ajax_link',
+      '#ajax' => [
+        'wrapper' => 'myDiv',
+        'method' => 'html',
+      ],
+    ];
+    return $build;
+  }
+
+  /**
+   *
+   */
+  public function ajaxExampleRenderLink() {
+    $attachments['#attached']['library'][] = 'system/drupal.ajax';
+    $explanation = $this->t("
+The link below has the <i>use-ajax</i> class applied to it, 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.");
+    $output = "<div>" . $explanation . "</div>";
+    // The use-ajax class is special, so that the link will call without causing
+    // a page reload. Note the /nojs portion of the path - if javascript is
+    // enabled, this part will be stripped from the path before it is called.
+    $value = ['attributes' => ['class' => ['use-ajax']]];
+    $url = Url::fromUri('internal:/ajax_link_callback/nojs/', $value);
+    $link = Link::fromTextAndUrl($this->t('Click here'), $url)->toString();
+    $output .= "<div id='myDiv'></div><div>$link</div>";
+    return $output;
+  }
+
+  /**
+   * 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 $type
+   *   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 ajaxlinkresponse($type = 'ajax') {
+    if ($type == 'ajax') {
+      $output = $this->t("This is some content delivered via AJAX");
+      $response = new AjaxResponse();
+      $response->addCommand(new AppendCommand('#myDiv', $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;
+    }
+    else {
+      $output = $this->t("This is some content delivered via a page load.");
+      return $output;
+    }
+  }
+
+  /**
+   * It works simply by searching usernames (and of course in Drupal usernames
+   * are unique, so can be used for identifying a record.)
+   *
+   * The returned $matches array has
+   * * key: string which will be displayed once the autocomplete is selected
+   * * value: the value which will is displayed in the autocomplete pulldown.
+   *
+   * In the simplest cases (see drupal/ajax_example/form/AjaxExampleAutocomplete.php)
+   * these are the same, andnothing needs to be done. However, more complicated
+   * autocompletes require more work. Here we demonstrate the difference by
+   * displaying the UID along with the username in the dropdown.
+   *
+   * In the end, though, we'll be doing something with the value that ends up in
+   * the textfield, so it needs to uniquely identify the record we want to access.
+   * This is demonstrated in ajax_example_unique_autocomplete().
+   *
+   * @param string $string
+   *   The string that will be searched.
+   */
+  public function ajax_example_simple_user_autocomplete_callback(Connection $connection, $string = "") {
+    $this->connection = Connection::$connection;
+    $matches = [];
+
+    if ($string) {
+
+      $result = $this->connection->select('users')
+        ->fields('users', ['name', 'uid'])
+        ->condition('name', $this->connection->like($string) . '%', 'LIKE')
+        ->range(0, 10)
+        ->execute();
+      foreach ($result as $user) {
+        // In the simplest case (see user_autocomplete), the key and the value are
+        // the same. Here we'll display the uid along with the username in the
+        // dropdown.
+        $matches[] = ['value' => $user->name, 'label' => $user->uid];
+        $matches[$user->name] = SafeMarkup::check_plain($user->name) . " (uid=$user->uid)";
+      }
+    }
+
+    return new JsonResponse($matches);
+  }
+
+  /**
+   * Autocomplete callback for nodes by title.
+   *
+   * Searches for a node by title, but then identifies it by nid, so the actual
+   * returned value can be used later by the form.
+   *
+   * The returned $matches array has
+   * - key: The title, with the identifying nid in brackets, like "Some node
+   *   title [3325]"
+   * - value: the title which will is displayed in the autocomplete pulldown.
+   *
+   * Note that we must use a key style that can be parsed successfully and
+   * unambiguously. For example, if we might have node titles that could have
+   * [3325] in them, then we'd have to use a more restrictive token.
+   *
+   * @param string $string
+   *   The string that will be searched.
+   */
+  public function ajax_example_unique_node_autocomplete_callback($string = "") {
+    $this->connection = Connection::$connection;
+    $matches = [];
+    if ($string) {
+
+      $result = $this->connection->select('node')
+        ->fields('node', ['nid', 'title'])
+        ->condition('title', $this->connection->escapeLike($string) . '%', 'LIKE')
+        ->range(0, 10)
+        ->execute();
+      foreach ($result as $node) {
+        $matches[$node->title . " [$node->nid]"] = SafeMarkup::check_plain($node->title);
+      }
+    }
+
+    return new JsonResponse($matches);
+  }
+
+  /**
+   *
+   */
+  public function ajax_example_node_by_author_node_autocomplete_callback($author_uid, $string = "") {
+    $this->connection = Connection::$connection;
+    $matches = [];
+    if ($author_uid > 0 && trim($string)) {
+      $db = Database::getConnection();
+      $result = $db->select('node')
+        ->fields('node', ['nid', 'title'])
+        ->condition('uid', $author_uid)
+        ->condition('title', $db->escapeLike($string) . '%', 'LIKE')
+        ->range(0, 10)
+        ->execute();
+      foreach ($result as $node) {
+        $matches[$node->title . " [$node->nid]"] = SafeMarkup::check_plain($node->title);
+      }
+    }
+
+    return new JsonResponse($matches);
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleAddMore.php b/ajax_example/src/Form/AjaxExampleAddMore.php
new file mode 100644
index 0000000..8c3cf0e
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleAddMore.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Link;
+
+/**
+ *
+ */
+class AjaxExampleAddMore extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_addmore';
+  }
+
+  /**
+   * This example shows a button to "add more" - add another textfield, and
+   * the corresponding "remove" button.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $no_js_use = FALSE) {
+    $url = Url::fromUri('internal:/examples/ajax-example/add-more-nojs/');
+    $link = Link::fromTextAndUrl($this->t('non-js version'), $url)->toString();
+
+    // Prepare link for multiple arguments.
+    $urltwo = Url::fromUri('internal:/examples/ajax_example/add-more');
+    $linktwo = Link::fromTextAndUrl($this->t('AJAX version'), $urltwo)->toString();
+    $form['description'] = [
+      '#markup' => t('This example shows an add-more and a remove-last button. The @link does it without page reloads; the @link2 is the same code but simulates a non-javascript environment, showing it with page reloads.',
+      ['@link' => $linktwo, '@link2' => $link]),
+
+    ];
+
+    // Because we have many fields with the same values, we have to set
+    // #tree to be able to access them.
+    $name_field = $form_state->get('num_names');
+    $form['#tree'] = TRUE;
+    $form['names_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => t('People coming to the picnic'),
+    // Set up the wrapper so that AJAX will be able to replace the fieldset.
+      '#prefix' => '<div id="names-fieldset-wrapper">',
+      '#suffix' => '</div>',
+    ];
+
+    // Build the fieldset with the proper number of names. We'll use
+    // $form_state['num_names'] to determine the number of textfields to build.
+    if (empty($name_field)) {
+      $name_field = $form_state->set('num_names', 1);
+    }
+    for ($i = 0; $i < $name_field; $i++) {
+      $form['names_fieldset']['name'][$i] = [
+        '#type' => 'textfield',
+        '#title' => t('Name'),
+      ];
+    }
+    $form['names_fieldset']['add_name'] = [
+      '#type' => 'submit',
+      '#value' => t('Add one more'),
+      '#submit' => ['::ajax_example_add_more_add_one'],
+      '#ajax' => [
+        'callback' => '::prompt',
+        'wrapper' => 'names-fieldset-wrapper',
+      ],
+    ];
+    if ($form['num_names'] > 1) {
+      $form['names_fieldset']['remove_name'] = [
+        '#type' => 'submit',
+        '#value' => t('Remove one'),
+        '#submit' => ['::ajax_example_add_more_remove_one'],
+        '#ajax' => [
+          'callback' => '::prompt',
+          'wrapper' => 'names-fieldset-wrapper',
+        ],
+      ];
+    }
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => t('Submit'),
+    ];
+
+    // 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.
+      if (!empty($form['names_fieldset']['remove_name']['#ajax'])) {
+        unset($form['names_fieldset']['remove_name']['#ajax']);
+      }
+      unset($form['names_fieldset']['add_name']['#ajax']);
+    }
+
+    return $form;
+  }
+
+  /**
+   * Callback for both ajax-enabled buttons.
+   *
+   * Selects and returns the fieldset with the names in it.
+   */
+  public function prompt($form, $form_state) {
+    return $form['names_fieldset'];
+  }
+
+  /**
+   * Submit handler for the "add-one-more" button.
+   *
+   * Increments the max counter and causes a rebuild.
+   */
+  public function ajax_example_add_more_add_one($form, &$form_state) {
+    $name_field = $form_state->get('num_names');
+    $add_button = $name_field + 1;
+    $form_state->set('num_names', $add_button);
+    $form_state->setRebuild();
+  }
+
+  /**
+   * Submit handler for the "remove one" button.
+   *
+   * Decrements the max counter and causes a form rebuild.
+   */
+  public function ajax_example_add_more_remove_one($form, &$form_state) {
+    $name_field = $form_state->get('num_names');
+    if ($name_field > 1) {
+      $remove_button = $name_field - 1;
+      $form_state->set('num_names', $remove_button);
+    }
+    $form_state->setRebuild(TRUE);
+  }
+
+  /**
+   * Final submit handler.
+   *
+   * Reports what values were finally set.
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $output = t('These people are coming to the picnic: @names',
+    [
+      '@names' => implode(', ', $form_state->getValue(['names_fieldset', 'name'])),
+    ]
+      );
+    // Sets a message to display to the user.
+    drupal_set_message($output);
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleAdvancedCommands.php b/ajax_example/src/Form/AjaxExampleAdvancedCommands.php
new file mode 100644
index 0000000..795c46d
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleAdvancedCommands.php
@@ -0,0 +1,439 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Core\Ajax\HtmlCommand;
+use Drupal\Core\Ajax\AlertCommand;
+use Drupal\Core\Ajax\AppendCommand;
+use Drupal\Core\Ajax\BeforeCommand;
+use Drupal\Core\Ajax\AddCssCommand;
+use Drupal\Core\Ajax\AfterCommand;
+use Drupal\Core\Ajax\ChangedCommand;
+use Drupal\Core\Ajax\DataCommand;
+use Drupal\Core\Ajax\PrependCommand;
+use Drupal\Core\Ajax\RemoveCommand;
+use Drupal\Core\Ajax\RestripeCommand;
+
+/**
+ * @file
+ * AJAX Commands examples.
+ *
+ * This demonstrates each of the
+ * new AJAX commands. This is consolidated into a dense page because
+ * it's advanced material and because it would spread itself all over creation
+ * otherwise.
+ */
+/**
+ *
+ */
+class AjaxExampleAdvancedCommands extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_Advanced';
+  }
+
+  /**
+   * Form to display the AJAX Commands.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form = [];
+    $form['intro'] = [
+      '#type' => 'markup',
+      '#markup' => $this->t("<div>Demonstrates how AJAX commands can be used.</div>"),
+    ];
+
+    // Shows the 'after' command with a callback generating commands.
+    $form['after_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("This shows the Ajax 'after' command. Click to put something below the div that says 'Something can be inserted after this'"),
+    ];
+
+    $form['after_command_example_fieldset']['after_command_example'] = [
+      '#value' => $this->t("AJAX 'After': Click to put something after the div"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_after_callback',
+      ],
+      '#suffix' => "<div id='after_div'>Something can be inserted after this</div>
+                        <div id='after_status'>'After' Command Status: Unknown</div>",
+    ];
+
+    // Shows the 'alert' command.
+    $form['alert_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("Demonstrates the AJAX 'alert' command. Click the button."),
+    ];
+    $form['alert_command_example_fieldset']['alert_command_example'] = [
+      '#value' => $this->t("AJAX 'Alert': Click to alert"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_alert_callback',
+      ],
+    ];
+
+    // Shows the 'append' command.
+    $form['append_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("This shows the Ajax 'append' command. Click to put something below the div that says 'Something can be inserted after this'"),
+    ];
+
+    $form['append_command_example_fieldset']['append_command_example'] = [
+      '#value' => $this->t("AJAX 'Append': Click to append something"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_append_callback',
+      ],
+      '#suffix' => "<div id='append_div'>Something can be appended inside this div... </div>
+                        <div id='append_status'>'After' Command Status: Unknown</div>",
+    ];
+
+    // Shows the 'before' command.
+    $form['before_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("This shows the Ajax 'before' command."),
+    ];
+
+    $form['before_command_example_fieldset']['before_command_example'] = [
+      '#value' => $this->t("AJAX 'before': Click to put something before the div"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_before_callback',
+      ],
+      '#suffix' => "<div id='before_div'>Something can be inserted before this</div>
+                        <div id='before_status'>'before' Command Status: Unknown</div>",
+    ];
+
+    // Shows the 'changed' command.
+    $form['changed_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("Demonstrates the AJAX 'changed' command. If region is 'changed', it is marked with CSS. This example also puts an asterisk by changed content."),
+    ];
+
+    $form['changed_command_example_fieldset']['changed_command_example'] = [
+      '#title' => $this->t("AJAX changed: If checked, div is marked as changed."),
+      '#type' => 'checkbox',
+      '#default_value' => FALSE,
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_changed_callback',
+      ],
+      '#suffix' => "<div id='changed_div'> <div id='changed_div_mark_this'>This div can be marked as changed or not.</div></div>
+                        <div id='changed_status'>Status: Unknown</div>",
+    ];
+
+    // Shows the AJAX 'css' command.
+    $form['css_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("Demonstrates the AJAX 'css' command."),
+    ];
+
+    $form['css_command_example_fieldset']['css_command_example'] = [
+      '#title' => $this->t("AJAX CSS: Choose the color you'd like the '#box' div to be."),
+      '#type' => 'select',
+    // '#default_value' => 'green',.
+      '#options' => ['green' => 'green', 'blue' => 'blue'],
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_css_callback',
+      ],
+      '#suffix' => "<div id='css_div' style='height: 50px; width: 50px; border: 1px solid black'> box</div>
+                        <div id='css_status'>Status: Unknown</div>",
+    ];
+
+    // Shows the AJAX 'data' command. But there is no use of this information,
+    // as this would require a javascript client to use the data.
+    $form['data_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("Demonstrates the AJAX 'data' command."),
+    ];
+
+    $form['data_command_example_fieldset']['data_command_example'] = [
+      '#title' => $this->t("AJAX data: Set a key/value pair on a selector."),
+      '#type' => 'textfield',
+      '#default_value' => 'color=green',
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_data_callback',
+      ],
+      '#suffix' => "<div id='data_div'>This div should have key='time'/value='a time string' attached.</div>
+                        <div id='data_status'>Status: Unknown</div>",
+    ];
+
+    // Shows the AJAX 'html' command.
+    $form['html_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("Demonstrates the AJAX 'html' command."),
+    ];
+
+    $form['html_command_example_fieldset']['html_command_example'] = [
+      '#title' => $this->t("AJAX html: Replace the HTML in a selector."),
+      '#type' => 'textfield',
+      '#default_value' => 'new value',
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_html_callback',
+      ],
+      '#suffix' => "<div id='html_div'>Original contents</div>
+                        <div id='html_status'>Status: Unknown</div>",
+    ];
+
+    // Shows the AJAX 'prepend' command.
+    $form['prepend_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("This shows the AJAX 'prepend' command. Click to put something below the div that says 'Something can be inserted after this'"),
+    ];
+
+    $form['prepend_command_example_fieldset']['prepend_command_example'] = [
+      '#value' => $this->t("AJAX 'prepend': Click to prepend something"),
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_prepend_callback',
+      ],
+      '#suffix' => "<div id='prepend_div'>Something can be prepended to this div... </div>
+                        <div id='prepend_status'>'After' Command Status: Unknown</div>",
+    ];
+
+    // Shows the AJAX 'remove' command.
+    $form['remove_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("Shows the Ajax 'remove' command."),
+    ];
+
+    $form['remove_command_example_fieldset']['remove_command_example'] = [
+      '#title' => $this->t("AJAX 'remove': Check to remove text. Uncheck to add it back."),
+      '#type' => 'checkbox',
+      '#default_value' => FALSE,
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_remove_callback',
+      ],
+      '#suffix' => "<div id='remove_div'><div id='remove_text'>text to be removed</div></div>
+                        <div id='remove_status'>'After' Command Status: Unknown</div>",
+    ];
+
+    // Show off the AJAX 'restripe' command. Also shows classic AJAX replacement
+    // on the "how many rows" processing.
+    $form['restripe_command_example_fieldset'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t("Demonstrates the Ajax 'restripe' command."),
+    ];
+
+    $form['restripe_command_example_fieldset']['restripe_num_rows'] = [
+      '#type' => 'select',
+      '#default_value' => !empty($form_state->getValue('restripe_num_rows')) ? $form_state->getValue('restripe_num_rows') : 1,
+      '#options' => [1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10],
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_restripe_num_rows',
+        'method' => 'replace',
+        'wrapper' => 'restripe_table',
+      ],
+    ];
+    $form['restripe_command_example_fieldset']['restripe_restripe'] = [
+      '#type' => 'submit',
+      '#value' => $this->t("Restripe the table"),
+      '#ajax' => [
+        'callback' => '::ajax_example_advanced_commands_restripe_callback',
+      ],
+      '#suffix' => "<div id='restripe_div'>
+                  <table id='restripe_table' style='border: 1px solid black' >
+                  <tr id='table-first'><td>first row</td></tr>
+                  </table>
+                  </div>
+                        <div id='restripe_status'>'Restripe' Command Status: Unknown</div>",
+
+    ];
+
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Submit'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * Callback for 'after'.
+   *
+   * @see Drupal\Core\Ajax\AfterCommand;
+   */
+  public function ajax_example_advanced_commands_after_callback($form, $form_state) {
+    $selector = '#after_div';
+    $response = new AjaxResponse();
+    $response->addCommand(new AfterCommand($selector, "New 'after'..."));
+    $response->addCommand(new ReplaceCommand("#after_status", "Updated after_command_example" . date('r') . ""));
+    return $response;
+  }
+
+  /**
+   * Callback for 'alert'.
+   *
+   * @see Drupal\Core\Ajax\AlertCommand;
+   */
+  public function ajax_example_advanced_commands_alert_callback($form, $form_state) {
+    $response = new AjaxResponse();
+    $response->addCommand(new AlertCommand("Alert requested at " . date('r')));
+    return $response;
+  }
+
+  /**
+   * Callback for 'append'.
+   *
+   * @see Drupal\Core\Ajax\AppendCommand;
+   */
+  public function ajax_example_advanced_commands_append_callback($form, $form_state) {
+    $selector = '#append_div';
+    $response = new AjaxResponse();
+    $response->addCommand(new AppendCommand($selector, "Stuff..."));
+    $response->addCommand(new ReplaceCommand("#append_status", "<div id='append_status'>Updated append_command_example " . date('r') . "</div>"));
+    return $response;
+  }
+
+  /**
+   * Callback for 'before'.
+   *
+   * @see Drupal\Core\Ajax\BeforeCommand;
+   */
+  public function ajax_example_advanced_commands_before_callback($form, $form_state) {
+    $selector = '#before_div';
+    $response = new AjaxResponse();
+    $response->addCommand(new BeforeCommand($selector, "New 'before'..."));
+    $response->addCommand(new ReplaceCommand("#before_status", "<div id='before_status'>Updated before_command_example " . date('r') . "</div>"));
+
+    return $response;
+  }
+
+  /**
+   * Callback for 'changed'.
+   *
+   * @see Drupal\Core\Ajax\ChangedCommand;
+   */
+  public function ajax_example_advanced_commands_changed_callback($form, $form_state) {
+    $checkbox_value = $form_state->getValue('changed_command_example');
+    $checkbox_value_string = $checkbox_value ? "TRUE" : "FALSE";
+    $response = new AjaxResponse();
+    if ($checkbox_value) {
+      $response->addCommand(new ChangedCommand('#changed_div', '#changed_div_mark_this'));
+    }
+    else {
+      $response->addCommand(new ReplaceCommand('#changed_div', "<div id='changed_div'> <div id='changed_div_mark_this'>This div can be marked as changed or not.</div></div>"));
+    }
+    $response->addCommand(new ReplaceCommand("#changed_status", "<div id='changed_status'>Updated changed_command_example to $checkbox_value_string: " . date('r') . "</div>"));
+    return $response;
+  }
+
+  /**
+   * Callback for 'css'.
+   *
+   * @see Drupal\Core\Ajax\AddCssCommand;
+   */
+  public function ajax_example_advanced_commands_css_callback($form, $form_state) {
+    $selector = '#css_div';
+    $color = $form_state->getValue('css_command_example');
+    $response = new AjaxResponse();
+    $response->addCommand(new AddCssCommand($selector, ['background-color' => $color]));
+    $response->addCommand(new ReplaceCommand("#css_status", "<div id='css_status'>Updated css_command_example to '{$color}' " . date('r') . "</div>"));
+    return $response;
+  }
+
+  /**
+   * Callback for 'data'.
+   *
+   * @see Drupal\Core\Ajax\DataCommand
+   */
+  public function ajax_example_advanced_commands_data_callback($form, $form_state) {
+    $selector = '#data_div';
+    $text = $form_state->getValue('data_command_example');
+    list($key, $value) = preg_split('/=/', $text);
+
+    $response = new AjaxResponse();
+    $response->addCommand(new DataCommand($selector, $key, $value));
+    $response->addCommand(new ReplaceCommand("#data_status", "<div id='data_status'>Updated data_command_example with key=$key, value=$value; " . date('r') . "</div>"));
+    return $response;
+  }
+
+  /**
+   * Callback for 'html'.
+   *
+   * @see Drupal\Core\Ajax\HtmlCommand
+   */
+  public function ajax_example_advanced_commands_html_callback($form, $form_state) {
+    $text = $form_state->getValue('html_command_example');
+    $response = new AjaxResponse();
+    $response->addCommand(new HtmlCommand('#html_div', $text));
+    $response->addCommand(new ReplaceCommand("#html_status", "<div id='html_status'>Updated html_command_example with text=$text;  " . date('r') . "</div>"));
+    return $response;
+  }
+
+  /**
+   * Callback for 'prepend'.
+   *
+   * @see Drupal\Core\Ajax\PrependCommand
+   */
+  public function ajax_example_advanced_commands_prepend_callback($form, $form_state) {
+    $response = new AjaxResponse();
+    $response->addCommand(new PrependCommand('#prepend_div', "Prepended Stuff..."));
+    $response->addCommand(new ReplaceCommand("#prepend_status", "<div id='prepend_status'>Updated prepend_command_example " . date('r') . "</div>"));
+
+    return $response;
+  }
+
+  /**
+   * Callback for 'remove'.
+   *
+   * @see Drupal\Core\Ajax\RemoveCommand
+   */
+  public function ajax_example_advanced_commands_remove_callback($form, $form_state) {
+    $response = new AjaxResponse();
+    $should_remove = $form_state->getValue('remove_command_example');
+    $should_remove_string = $should_remove ? 'TRUE' : 'FALSE';
+    if ($should_remove) {
+      $response->addCommand(new RemoveCommand('#remove_text'));
+
+    }
+    else {
+      $response->addCommand(new HtmlCommand('#remove_div', "<div id='remove_text'>text to be removed</div>"));
+    }
+    $response->addCommand(new ReplaceCommand("#remove_status", "<div id='remove_status'>Updated remove_command_example (value={$should_remove_string} " . date('r') . "</div>"));
+
+    return $response;
+
+  }
+
+  /**
+   * Callback for 'restripe'.
+   *
+   * Rebuilds the table with the selected number of rows.
+   */
+  public function ajax_example_advanced_commands_restripe_num_rows($form, $form_state) {
+    $num_rows = $form_state->getValue('restripe_num_rows');
+    $output = "<table id='restripe_table' style='border: 1px solid black'>";
+    for ($i = 1; $i <= $num_rows; $i++) {
+      $output .= "<tr><td>Row $i</td></tr>";
+    }
+    $output .= "</table>";
+    return $output;
+  }
+
+  /**
+   * Callback for 'restripe'.
+   *
+   * @see Drupal\Core\Ajax\RestripeCommand
+   */
+  public function ajax_example_advanced_commands_restripe_callback($form, $form_state) {
+    $response = new AjaxResponse();
+    $response->addCommand(new RestripeCommand('#restripe_table'));
+    $response->addCommand(new ReplaceCommand("#restripe_status", "<div id='restripe_status'>Restriped table " . date('r') . "</div>"));
+
+    return $response;
+
+  }
+
+  /**
+   *
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleAutocheckboxes.php b/ajax_example/src/Form/AjaxExampleAutocheckboxes.php
new file mode 100644
index 0000000..4e72b32
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleAutocheckboxes.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Generate a changing number of checkboxes.
+ */
+class AjaxExampleAutocheckboxes extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_autocheckbox';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $num_checkboxes = !empty($form_state->getValue('howmany_select')) ? $form_state->getValue('howmany_select') : 1;
+    $form['howmany_select'] = [
+      '#title' => t('How many checkboxes do you want?'),
+      '#type' => 'select',
+      '#options' => [1 => 1, 2 => 2, 3 => 3, 4 => 4],
+      '#default_value' => $num_checkboxes,
+      '#ajax' => [
+        'callback' => '::prompt',
+        'wrapper' => 'checkboxes-div',
+      // 'method' defaults to replaceWith, but valid values also include
+      // append, prepend, before and after.
+      // 'method' => 'replaceWith',
+      // 'effect' defaults to none. Other valid values are 'fade' and 'slide'.
+      // See AjaxExampleAutoTextfields for an example of 'fade'.
+        'effect' => 'slide',
+      // 'speed' defaults to 'slow'. You can also use 'fast'
+      // or a number of milliseconds for the animation to last.
+      // 'speed' => 'slow',
+      // Don't show any throbber...
+        'progress' => ['type' => 'none'],
+      ],
+    ];
+
+    $form['checkboxes_fieldset'] = [
+      '#title' => t("Generated Checkboxes"),
+      // The prefix/suffix provide the div that we're replacing, named by
+      // #ajax['wrapper'] above.
+      '#prefix' => '<div id="checkboxes-div">',
+      '#suffix' => '</div>',
+      '#type' => 'fieldset',
+      '#description' => $this->t('This is where we get automatically generated checkboxes'),
+    ];
+
+    for ($i = 1; $i <= $num_checkboxes; $i++) {
+      $form['checkboxes_fieldset']["checkbox$i"] = [
+        '#type' => 'checkbox',
+        '#title' => "Checkbox $i",
+      ];
+    }
+
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Submit'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * Callback for autocheckboxes.
+   *
+   * Callback element needs only select the portion of the form to be updated.
+   * Since #ajax['callback'] return can be HTML or a renderable array (or an
+   * array of commands), we can just return a piece of the form.
+   * See @link examples/ajax-example/advanced-commands for more details
+   * on AJAX framework commands.
+   *
+   * @return array
+   *   Renderable array (the checkboxes fieldset)
+   */
+  public function prompt($form, FormStateInterface $form_state) {
+    return $form['checkboxes_fieldset'];
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleAutocomplete.php b/ajax_example/src/Form/AjaxExampleAutocomplete.php
new file mode 100644
index 0000000..98aa41d
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleAutocomplete.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * A simple autocomplete form which just looks up usernames in the user table.
+ */
+class AjaxExampleAutocomplete extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_autocomplete';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['info'] = [
+      '#markup' => '<div>' . t("This example does a simplest possible autocomplete by username. You'll need a few users on your system for it to make sense.") . '</div>',
+    ];
+
+    $form['user'] = [
+      '#type' => 'textfield',
+      '#title' => t('Choose a user (or a people, depending on your usage preference)'),
+    // The autocomplete path is provided in routing.
+      '#autocomplete_path' => 'examples/ajax_example/simple_user_autocomplete_callback',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleAutocompleteAuthor.php b/ajax_example/src/Form/AjaxExampleAutocompleteAuthor.php
new file mode 100644
index 0000000..0ea5d62
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleAutocompleteAuthor.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Database\Query\Condition;
+use Drupal\node\Entity\Node;
+use Drupal\node\Entity\User;
+
+/**
+ *
+ */
+class AjaxExampleAutocompleteAuthor extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_autocompleteauthor';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['intro'] = [
+      '#markup' => '<div>' . t("This example uses a user autocomplete to dynamically change a node title autocomplete using #ajax.
+      This is a way to get past the fact that we have no other way to provide context to the autocomplete function.
+      It won't work very well unless you have a few users who have created some content that you can search for.") . '</div>',
+    ];
+
+    $form['author'] = [
+      '#type' => 'textfield',
+      '#title' => t('Choose the username that authored nodes you are interested in'),
+    // Since we just need simple user lookup, we can use the simplest function
+    // of them all, user_autocomplete().
+      '#autocomplete_path' => 'user/autocomplete',
+      '#ajax' => [
+        'callback' => '::ajax_example_node_by_author_ajax_callback',
+        'wrapper' => 'autocomplete-by-node-ajax-replace',
+      ],
+    ];
+
+    // This form element with autocomplete will be replaced by #ajax whenever the
+    // author changes, allowing the search to be limited by user.
+    $form['node'] = [
+      '#type' => 'textfield',
+      '#title' => t('Choose a node by title'),
+      '#prefix' => '<div id="autocomplete-by-node-ajax-replace">',
+      '#suffix' => '</div>',
+      '#disabled' => TRUE,
+    ];
+
+    // When the author changes in the author field, we'll change the
+    // autocomplete_path to match.
+    if (!empty($form_state['values']['author'])) {
+      $author = user_load_by_name($form_state['values']['author']);
+      if (!empty($author)) {
+        $autocomplete_path = 'examples/ajax_example/node_by_author_autocomplete/' . $author->uid;
+        $form['node']['#autocomplete_path'] = $autocomplete_path;
+        $form['node']['#title'] = t('Choose a node title authored by %author', ['%author' => $author->name]);
+        $form['node']['#disabled'] = FALSE;
+      }
+    }
+
+    $form['actions'] = [
+      '#type' => 'actions',
+    ];
+
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => t('Submit'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $title = $form_state['values']['node'];
+    $author = $form_state['values']['author'];
+    $matches = [];
+
+    // We must have a valid user.
+    $account = user_load_by_name($author);
+    if (empty($account)) {
+      $form_state->setErrorByName($form['author'], t('You must choose a valid author username'));
+      return;
+    }
+    // This preg_match() looks for the last pattern like [33334] and if found
+    // extracts the numeric portion.
+    $result = preg_match('/\[([0-9]+)\]$/', $title, $matches);
+    if ($result > 0) {
+      // If $result is nonzero, we found a match and can use it as the index into
+      // $matches.
+      $nid = $matches[$result];
+      // Verify that it's a valid nid.
+      $node = Node::load($nid);
+      if (empty($node)) {
+        $form_state->setErrorByName($form['node'], t('Sorry, no node with nid %nid can be found', ['%nid' => $nid]));
+        return;
+      }
+      // BUT: Not everybody will have javascript turned on, or they might hit ESC
+      // and not use the autocomplete values offered. In that case, we can attempt
+      // to come up with a useful value. This is not absolutely necessary, and we
+      // *could* just emit a form_error() as below. Here we'll find the *first*
+      // matching title and assume that is adequate.
+      else {
+        $db = Database::getConnection();
+        $nid = $db->select('node')
+          ->fields('node', ['nid'])
+          ->condition('uid', $account->uid)
+          ->condition('title', $db->escapeLike($title) . '%', 'LIKE')
+          ->range(0, 1)
+          ->execute()
+          ->fetchField();
+      }
+
+      // Now, if we somehow found a nid, assign it to the node. If we failed, emit
+      // an error.
+      if (!empty($nid)) {
+        $form_state->setValue('node', $nid);
+      }
+      else {
+        $form_state->setErrorByName($form['node'], t('Sorry, no node starting with %title can be found', ['%title' => $title]));
+      }
+
+    }
+  }
+
+  /**
+   * AJAX callback for author form element.
+   */
+  public function ajax_example_node_by_author_ajax_callback($form, $form_state) {
+    return $form['node'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $node = Node::load($form_state->getValue('node'));
+    $account = User::load($node->uid);
+    drupal_set_message(t('You found node %nid with title !title_link, authored by !user_link',
+    [
+      '%nid' => $node->nid,
+      '!title_link' => l($node->title, 'node/' . $node->nid),
+      '!user_link' => theme('username', ['account' => $account]),
+    ]
+    ));
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleAutotextfields.php b/ajax_example/src/Form/AjaxExampleAutotextfields.php
new file mode 100644
index 0000000..4150f3f
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleAutotextfields.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Show/hide textfields based on AJAX-enabled checkbox clicks.
+ */
+class AjaxExampleAutotextfields extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_autotextfields';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['ask_first_name'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Ask me my first name'),
+      '#ajax' => [
+        'callback' => '::prompt',
+        'wrapper' => 'textfields',
+        'effect' => 'fade',
+      ],
+    ];
+    $form['ask_last_name'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Ask me my last name'),
+      '#ajax' => [
+        'callback' => '::prompt',
+        'wrapper' => 'textfields',
+        'effect' => 'fade',
+      ],
+    ];
+
+    $form['textfields'] = [
+      '#title' => $this->t("Generated text fields for first and last name"),
+      '#prefix' => '<div id="textfields">',
+      '#suffix' => '</div>',
+      '#type' => 'fieldset',
+      '#description' => t('This is where we put automatically generated textfields'),
+    ];
+
+    // Since checkboxes return TRUE or FALSE, we have to check that
+    // $form_state has been filled as well as what it contains.
+    if (!empty($form_state->getValue('ask_first_name')) && $form_state->getValue('ask_first_name')) {
+      $form['textfields']['first_name'] = [
+        '#type' => 'textfield',
+        '#title' => $this->t('First Name'),
+      ];
+    }
+    if (!empty($form_state->getValue('ask_last_name')) && $form_state->getValue('ask_last_name')) {
+      $form['textfields']['last_name'] = [
+        '#type' => 'textfield',
+        '#title' => $this->t('Last Name'),
+      ];
+    }
+
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Click Me'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * Callback for autotextfields.
+   *
+   * Selects the piece of the form we want to use as replacement text and returns
+   * it as a form (renderable array).
+   */
+  public function prompt($form, FormStateInterface $form_state) {
+    return $form['textfields'];
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleDependentDropdown.php b/ajax_example/src/Form/AjaxExampleDependentDropdown.php
new file mode 100644
index 0000000..cb1d6ee
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleDependentDropdown.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Repopulate a dropdown based on form state.
+ */
+class AjaxExampleDependentDropdown extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_dependentdropdown';
+  }
+
+  /**
+   * AJAX-based dropdown example form.
+   *
+   * A form with a dropdown whose options are dependent on a
+   * choice made in a previous dropdown.
+   *
+   * On changing the first dropdown, the options in the second
+   * are updated.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $options_first = _ajax_example_get_first_dropdown_options();
+    // If we have a value for the first dropdown from $form_state['values'] we use
+    // this both as the default value for the first dropdown and also as a
+    // parameter to pass to the function that retrieves the options for the
+    // second dropdown.
+    $selected = !empty($form_state->getValue('dropdown_first')) ? $form_state->getValue('dropdown_first') : key($options_first);
+
+    $form['dropdown_first'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Instrument Type'),
+      '#options' => $options_first,
+      '#default_value' => $selected,
+    // 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'.
+      // 'event' => 'change',.
+        'callback' => '::prompt',
+        'wrapper' => 'dropdown-second-replace',
+      ],
+    ];
+
+    $form['dropdown_second'] = [
+      '#type' => 'select',
+      '#title' => $options_first[$selected] . ' ' . $this->t('Instruments'),
+    // The entire enclosing div created here gets replaced when dropdown_first
+    // is changed.
+      '#prefix' => '<div id="dropdown-second-replace">',
+      '#suffix' => '</div>',
+    // When the form is rebuilt during ajax processing, the $selected variable
+    // will now have the new value and so the options will change.
+      '#options' => _ajax_example_get_second_dropdown_options($selected),
+      '#default_value' => !empty($form_state->getValue('dropdown_second')) ? $form_state->getValue('dropdown_second') : '',
+    ];
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Submit'),
+    ];
+    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. dropdown_first=@first, dropdown_second=@second', [
+          '@first' => $form_state->getValue('dropdown_first'),
+          '@second' => $form_state->getValue('dropdown_second'),
+        ]));
+        return;
+    }
+    // 'Choose' or anything else will cause rebuild of the form and present
+    // it again.
+    $form_state->setRebuild();
+  }
+
+  /**
+   * Handles switching the available regions based on the selected theme.
+   */
+  public function prompt($form, FormStateInterface $form_state) {
+    return $form['dropdown_second'];
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleDependentDropdownDegardes.php b/ajax_example/src/Form/AjaxExampleDependentDropdownDegardes.php
new file mode 100644
index 0000000..1a32b42
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleDependentDropdownDegardes.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Dropdown form based on previous choices.
+ *
+ * A form with a dropdown whose options are dependent on a choice made in a
+ * previous dropdown.
+ *
+ * On changing the first dropdown, the options in the second
+ * are updated. Gracefully degrades if no javascript.
+ *
+ * A bit of CSS and javascript is required. The CSS hides the "add more" button
+ * if javascript is not enabled. The Javascript snippet is really only used
+ * to enable us to present the form in degraded mode without forcing the user
+ * to turn off Javascript.  Both of these are loaded by using the
+ * #attached FAPI property, so it is a good example of how to use that.
+ *
+ * The extra argument $no_js_use is here only to allow presentation of this
+ * form as if Javascript were not enabled. ajax_example_menu() provides two
+ * ways to call this form, one normal ($no_js_use = FALSE) and one simulating
+ * Javascript disabled ($no_js_use = TRUE).
+ */
+class AjaxExampleDependentDropdownDegardes extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_dependentdropdowndegardes';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $no_js_use = FALSE) {
+    $options_first = _ajax_example_get_first_dropdown_options();
+
+    // If we have a value for the first dropdown from $form_state['values'] we use
+    // this both as the default value for the first dropdown and also as a
+    // parameter to pass to the function that retrieves the options for the
+    // second dropdown.
+    $selected = !empty($form_state->getValue('dropdown_first')) ? $form_state->getValue('dropdown_first') : key($options_first);
+
+    // 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['dropdown_first_fieldset'] = [
+      '#type' => 'details',
+      '#open' => TRUE,
+    ];
+    $form['dropdown_first_fieldset']['dropdown_first'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Instrument Type'),
+      '#options' => $options_first,
+      '#attributes' => ['class' => ['enabled-for-ajax']],
+
+    // The '#ajax' property allows us to bind a callback to the server whenever
+    // this form element changes. See ajax_example_autocheckboxes and
+    // ajax_example_dependent_dropdown in ajax_example.module for more details.
+      '#ajax' => [
+        'callback' => '::prompt',
+        'wrapper' => 'dropdown-second-replace',
+      ],
+    ];
+
+    // 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. This is for demonstration purposes only.
+    if ($no_js_use) {
+      unset($form['dropdown_first_fieldset']['dropdown_first']['#ajax']);
+    }
+
+    // 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['dropdown_first_fieldset']['continue_to_second'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Choose'),
+      '#attributes' => ['class' => ['next-button']],
+    ];
+
+    $form['dropdown_second_fieldset'] = [
+      '#type' => 'details',
+      '#open' => TRUE,
+    ];
+    $form['dropdown_second_fieldset']['dropdown_second'] = [
+      '#type' => 'select',
+      '#title' => $options_first[$selected] . ' ' . $this->t('Instruments'),
+      '#prefix' => '<div id="dropdown-second-replace">',
+      '#suffix' => '</div>',
+      '#attributes' => ['class' => ['enabled-for-ajax']],
+    // When the form is rebuilt during processing (either AJAX or multistep),
+    // the $selected variable will now have the new value and so the options
+    // will change.
+      '#options' => _ajax_example_get_second_dropdown_options($selected),
+    ];
+    $form['dropdown_second_fieldset']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('OK'),
+    // This class allows attached js file to override the disabled attribute,
+    // since it's not necessary in ajax-enabled form.
+      '#attributes' => ['class' => ['enabled-for-ajax']],
+    ];
+
+    // Disable dropdown_second if a selection has not been made on dropdown_first.
+    if (empty($form_state->getValue('dropdown_first'))) {
+      $form['dropdown_second_fieldset']['dropdown_second']['#disabled'] = TRUE;
+      $form['dropdown_second_fieldset']['submit']['#disabled'] = FALSE;
+      $form['dropdown_second_fieldset']['dropdown_second']['#description'] = $this->t('You must make your choice on the first dropdown before changing this second one.');
+    }
+    return $form;
+  }
+
+  /**
+   * Selects just the second dropdown to be returned for re-rendering.
+   *
+   * @return array
+   *   Renderable array (the second dropdown).
+   */
+  public function prompt(array $form, FormStateInterface $form_state) {
+    return $form['dropdown_second_fieldset']['dropdown_second'];
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    // Switch ($form_state->getTriggeringElement()) {
+    // case t('OK'):
+    // Submit: We're done.
+    $trigger = $form_state->getTriggeringElement()['#value'];
+    switch ($trigger) {
+      case 'Choose':
+        $form_state->setRebuild();
+        break;
+
+      case 'OK':
+        drupal_set_message(t('Your values have been submitted. dropdown_first=@first, dropdown_second=@second', ['@first' => $form_state->getValue('dropdown_first'), '@second' => $form_state->getValue('dropdown_second')]));
+        break;
+    }
+    $form_state->setRebuild();
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleDynamicSectionsDegardes.php b/ajax_example/src/Form/AjaxExampleDynamicSectionsDegardes.php
new file mode 100644
index 0000000..8e55141
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleDynamicSectionsDegardes.php
@@ -0,0 +1,196 @@
+<?php
+
+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.
+ *
+ * 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 AjaxExampleDynamicSectionsDegardes extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_dynamicsectiondegardes';
+  }
+
+  /**
+   *
+   */
+  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();
+    // 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['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.
+      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
+      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]),
+    ];
+    $form['question_type_select'] = [
+      '#type' => 'select',
+      '#title' => t('Question style'),
+      '#options' => [
+        'Choose question style' => 'Choose question style',
+        'Multiple Choice' => 'Multiple Choice',
+        'True/False' => 'True/False',
+        'Fill-in-the-blanks' => 'Fill-in-the-blanks',
+      ],
+
+      '#ajax' => [
+        'wrapper' => 'questions-fieldset-wrapper',
+        'callback' => '::prompt',
+      ],
+    ];
+    // 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.
+      '#limit_validation_errors' => [],
+      '#validate' => [],
+    ];
+
+    // 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.
+    if ($no_js_use) {
+      // 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.
+    $form['questions_fieldset'] = [
+      '#type' => 'fieldset',
+    // These provide the wrapper referred to in #ajax['wrapper'] above.
+      '#prefix' => '<div id="questions-fieldset-wrapper">',
+      '#suffix' => '</div>',
+    ];
+    if (!empty($form_state->getValue('question_type_select'))) {
+
+      $form['questions_fieldset']['question'] = [
+        '#markup' => t('Who was the first president of the U.S.?'),
+      ];
+      $question_type = $form_state->getValue('question_type_select');
+
+      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');
+      print_r($answers);
+      // 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.
+   *
+   * This just selects and returns the questions_fieldset.
+   */
+  public function prompt($form, $form_state) {
+    return $form['questions_fieldset'];
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleNodeFormAlter.php b/ajax_example/src/Form/AjaxExampleNodeFormAlter.php
new file mode 100644
index 0000000..bf1a77d
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleNodeFormAlter.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Database\Connection;
+
+/**
+ *
+ */
+class AjaxExampleNodeFormAlter extends FormBase {
+
+  protected $connection;
+
+  /**
+   *
+   */
+  public function __construct(Connection $connection) {
+    $this->connection = $connection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_modeformalter';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $node = $form['#node'];
+    $form['ajax_example_1'] = [
+      '#type' => 'checkbox',
+      '#title' => t('AJAX Example 1'),
+      '#description' => t('Enable to show second field.'),
+      '#default_value' => $node->ajax_example['example_1'],
+      '#ajax' => [
+        'callback' => '::ajax_example_form_node_callback',
+        'wrapper' => 'ajax-example-form-node',
+        'effect' => 'fade',
+      ],
+    ];
+    $form['container'] = [
+      '#prefix' => '<div id="ajax-example-form-node">',
+      '#suffix' => '</div>',
+    ];
+
+    // If the state values exist and 'ajax_example_1' state value is 1 or
+    // if the state values don't exist and 'example1' variable is 1 then
+    // display the ajax_example_2 field.
+    if (!empty($form_state->getValue('ajax_example_1')) && $form_state->getValue('ajax_example_1') == 1
+      || empty($form_state->getValue()) && $node->ajax_example['example_1']) {
+
+      $form['container']['ajax_example_2'] = [
+        '#type' => 'textfield',
+        '#title' => t('AJAX Example 2'),
+        '#description' => t('AJAX Example 2'),
+        '#default_value' => empty($form_state->getValue('ajax_example_2')) ? $node->ajax_example['example_2'] : $form_state->getValue('ajax_example_2'),
+      ];
+    }
+  }
+
+  /**
+   * Callback of ajax.
+   */
+  public function ajax_example_form_node_callback($form, $form_state) {
+    return $form['container'];
+  }
+
+  /**
+   *
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $values = $form_state->getValue();
+    // Move the new data into the node object.
+    $node->ajax_example['example_1'] = $values['ajax_example_1'];
+    // Depending on the state of ajax_example_1; it may not exist.
+    $node->ajax_example['example_2'] = isset($values['ajax_example_2']) ? $values['ajax_example_2'] : '';
+  }
+
+  /**
+   *
+   */
+  public function ajax_example_node_prepare($node) {
+    if (empty($node->ajax_example)) {
+      // Set default values, since this only runs when adding a new node.
+      $node->ajax_example['example_1'] = 0;
+      $node->ajax_example['example_2'] = '';
+    }
+  }
+
+  /**
+   * Implements hook_node_load().
+   *
+   * @see ajax_example_form_node_form_alter()
+   */
+  public function ajax_example_node_load($nodes, $types) {
+    $result = $this->connection->query('SELECT * FROM {ajax_example_node_form_alter} WHERE nid IN(:nids)', [':nids' => array_keys($nodes)])->fetchAllAssoc('nid');
+
+    foreach ($nodes as &$node) {
+      $node->ajax_example['example_1']
+      = isset($result[$node->nid]->example_1) ?
+      $result[$node->nid]->example_1 : 0;
+      $node->ajax_example['example_2']
+      = isset($result[$node->nid]->example_2) ?
+      $result[$node->nid]->example_2 : '';
+    }
+  }
+
+  /**
+   * Implements hook_node_insert().
+   *
+   * @see ajax_example_form_node_form_alter()
+   */
+  public function ajax_example_node_insert($node) {
+    if (isset($node->ajax_example)) {
+      $this->connection->insert('ajax_example_node_form_alter')
+        ->fields([
+          'nid' => $node->nid,
+          'example_1' => $node->ajax_example['example_1'],
+          'example_2' => $node->ajax_example['example_2'],
+        ])
+        ->execute();
+    }
+  }
+
+  /**
+   * Implements hook_node_update().
+   *
+   * @see ajax_example_form_node_form_alter()
+   */
+  public function ajax_example_node_update($node) {
+    if ($this->connection->select('ajax_example_node_form_alter', 'a')->fields('a')->condition('nid', $node->nid, '=')->execute()->fetchAssoc()) {
+      $this->connection->update('ajax_example_node_form_alter')
+        ->fields([
+          'example_1' => $node->ajax_example['example_1'],
+          'example_2' => $node->ajax_example['example_2'],
+        ])
+        ->condition('nid', $node->nid)
+        ->execute();
+    }
+    else {
+      // Cleaner than doing it again.
+      ajax_example_node_insert($node);
+    }
+  }
+
+  /**
+   * Implements hook_node_delete().
+   *
+   * @see ajax_example_form_node_form_alter()
+   */
+  public function ajax_example_node_delete($node) {
+    $this->connection->delete('ajax_example_node_form_alter')
+      ->condition('nid', $node->nid)
+      ->execute();
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleProgressBar.php b/ajax_example/src/Form/AjaxExampleProgressBar.php
new file mode 100644
index 0000000..0ca8653
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleProgressBar.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Ajax\HtmlCommand;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Progress bar example.
+ */
+class AjaxExampleProgressBar extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_progressbar';
+  }
+
+  /**
+   * Build a landing-page form for the progress bar example.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['time'] = \Drupal::time()->getRequestTime();
+    // We make a DIV which the progress bar can occupy. You can see this in use
+    // in ajax_example_progressbar_callback().
+    $form['status'] = [
+      '#title' => $this->t("progress-status"),
+    ];
+
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Submit'),
+      '#ajax' => [
+      // Here we set up our AJAX callback handler.
+        'callback' => '::prompt',
+      // Tell FormAPI about our progress bar.
+        'progress' => [
+          'type' => 'bar',
+          'message' => $this->t('Execute..'),
+        // Have the progress bar access this URL path.
+          'url' => Url::fromUri('internal:/examples/ajax_example/progressbar/progress/' . $form['time']),
+        // The time interval for the progress bar to check for updates.
+          'interval' => 1000,
+        ],
+      ],
+    ];
+
+    return $form;
+
+  }
+
+  /**
+   * Our submit handler.
+   *
+   * This handler spends some time changing a variable and sleeping, and then
+   * finally returns a form element which marks the #progress-status DIV as
+   * completed.
+   *
+   * While this is occurring, ajax_example_progressbar_progress() will be called
+   * a number of times by the client-sid JavaScript, which will poll the variable
+   * being set here.
+   *
+   * @see \Drupal\ajax_example\Controller\AjaxExampleController::progressbarProgress
+   */
+  public function prompt(array $form, FormStateInterface $form_state) {
+    $variable_name = 'example_progressbar_' . $form['time'];
+    $response = new AjaxResponse();
+    $config = \Drupal::config('ajaxexample.settings');
+    $config->set($variable_name, 10)->save();
+    sleep(2);
+    $config->set($variable_name, 40)->save();
+    sleep(2);
+    $config->set($variable_name, 70)->save();
+    sleep(2);
+    $config->set($variable_name, 90)->save();
+    sleep(2);
+    $response->addCommand(new HtmlCommand('#progress-status', $this->t('Executed.')));
+    return $response;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleSimplest.php b/ajax_example/src/Form/AjaxExampleSimplest.php
new file mode 100644
index 0000000..0b2aa3c
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleSimplest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ *
+ */
+class AjaxExampleSimplest 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' => '::prompt',
+        // 'wrapper' is the HTML id of the page element that will be replaced.
+        'wrapper' => 'replace_textfield_div',
+        // There are also several optional keys - see AjaxExampleAutoCheckboxes
+        // below for details on 'method', 'effect' and 'speed' and
+        // AjaxExampleDependentDropDown for 'event'.
+      ],
+    ];
+
+    // This entire form element will be replaced whenever 'changethis' is updated.
+    $form['replace_textfield'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t("Why"),
+      // The prefix/suffix provide the div that we're replacing, named by
+      // #ajax['wrapper'] above.
+      '#prefix' => '<div id="replace_textfield_div">',
+      '#suffix' => '</div>',
+    ];
+
+    // 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');
+    if (!empty($value)) {
+      $form['replace_textfield']['#description'] = $this->t("Say why you chose '@value'", ['@value' => $value]);
+    }
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * Handles switching the available regions based on the selected theme.
+   */
+  public function prompt($form, FormStateInterface $form_state) {
+    return $form['replace_textfield'];
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleSubmitDriven.php b/ajax_example/src/Form/AjaxExampleSubmitDriven.php
new file mode 100644
index 0000000..703d87c
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleSubmitDriven.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Submit a form without a page reload.
+ */
+class AjaxExampleSubmitDriven extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_autotextfields';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['box'] = [
+      '#type' => 'markup',
+      '#prefix' => '<div id="box">',
+      '#suffix' => '</div>',
+      '#markup' => '<h1>Initial markup for box</h1>',
+    ];
+
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#ajax' => [
+        'callback' => '::prompt',
+        'wrapper' => 'box',
+      ],
+      '#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 prompt(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['box'];
+    $element['#markup'] = "Clicked submit ({$form_state->getValue('op')}): " . date('c');
+    return $element;
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleUniquecomplete.php b/ajax_example/src/Form/AjaxExampleUniquecomplete.php
new file mode 100644
index 0000000..7f18260
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleUniquecomplete.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Drupal\ajax_example\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Database\Query\Condition;
+use Drupal\node\Entity\Node;
+
+/**
+ * An autocomplete form to look up nodes by title.
+ *
+ * An autocomplete form which looks up nodes by title in the node table,
+ * but must keep track of the nid, because titles are certainly not guaranteed
+ * to be unique.
+ */
+class AjaxExampleUniquecomplete extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'ajax_example_uniquecomplete';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['info'] = [
+      '#markup' => '<div>' . t("This example does a node autocomplete by title. The difference between this and a username autocomplete is that the node title may not be unique, so we have to use the nid for uniqueness, placing it in a parseable location in the textfield.") . '</div>',
+    ];
+
+    $form['node'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Choose a node by title'),
+    // The autocomplete path is provided in hook_menu in ajax_example.module.
+      '#autocomplete_path' => 'examples/ajax_example/unique_node_autocomplete_callback',
+    ];
+
+    $form['actions'] = [
+      '#type' => 'actions',
+    ];
+
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Submit'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $title = $form_state->getValue('node');
+    $matches = [];
+
+    // This preg_match() looks for the last pattern like [33334] and if found
+    // extracts the numeric portion.
+    $result = preg_match('/\[([0-9]+)\]$/', $title, $matches);
+    if ($result > 0) {
+      // If $result is nonzero, we found a match and can use it as the index into
+      // $matches.
+      $nid = $matches[$result];
+      // Verify that it's a valid nid.
+      $node = Node::load($nid);
+      if (empty($node)) {
+        $form_state->setErrorByName($form['node'], t('Sorry, no node with nid %nid can be found', ['%nid' => $nid]));
+        return;
+      }
+    }
+    // BUT: Not everybody will have javascript turned on, or they might hit ESC
+    // and not use the autocomplete values offered. In that case, we can attempt
+    // to come up with a useful value. This is not absolutely necessary, and we
+    // *could* just emit a form_error() as below.
+    else {
+      $db = Database::getConnection();
+      $nid = $db->select('node')
+        ->fields('node', ['nid'])
+        ->condition('title', $db->escapeLike($title) . '%', 'LIKE')
+        ->range(0, 1)
+        ->execute()
+        ->fetchField();
+    }
+
+    // Now, if we somehow found a nid, assign it to the node. If we failed, emit
+    // an error.
+    if (!empty($nid)) {
+      $form_state->setValue('node', $nid);
+    }
+    else {
+      $form_state->setErrorByName($form['node'], $this->t('Sorry, no node starting with %title can be found', ['%title' => $title]));
+    }
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $node = Node::load($form_state->getValue(['values', 'node']));
+    drupal_set_message($this->t('You found node %nid with title %title', ['%nid' => $node->nid, '%title' => $node->title]));
+  }
+
+}
diff --git a/ajax_example/src/Form/AjaxExampleWizard.php b/ajax_example/src/Form/AjaxExampleWizard.php
new file mode 100644
index 0000000..adffbd5
--- /dev/null
+++ b/ajax_example/src/Form/AjaxExampleWizard.php
@@ -0,0 +1,230 @@
+<?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;
+
+/**
+ *
+ */
+class AjaxExampleWizard 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();
+
+    // $form['#prefix'] = '<div id="wizard-form-wrapper">';
+    //    $form['#suffix'] = '</div>';
+    // 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 array $form_state
+   *   Form API form.
+   *
+   * @return array
+   *   Form array.
+   */
+  public function prompt($form, $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;
+    }
+
+  }
+
+}
diff --git a/ajax_example/templates/description.html.twig b/ajax_example/templates/description.html.twig
new file mode 100644
index 0000000..e668459
--- /dev/null
+++ b/ajax_example/templates/description.html.twig
@@ -0,0 +1,39 @@
+{#
+
+Description text for the Ajax Example.
+
+#}
+
+{% set simple_ajax_example = path('ajax_example.simplest') %}
+{% set ajax_generate_checkboxes = path('ajax_example.autocheckboxes') %}
+{% 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_degrade = path('ajax_example.dependent_dropdown_degardes') %}
+{% set ajax_dependent_dropdown_nojs = path('ajax_example.dependent_dropdown_degardes_nojava') %}
+{% set ajax_dynamic_dropdown = path('ajax_example.dynamic_sections') %}
+{% set ajax_dynamic_dropdown_nojs = path('ajax_example.dynamic_sections_nojava') %}
+{% set ajax_wizard_example = path('ajax_example.wizard') %}
+{% set ajax_wizard_example_nojs = path('ajax_example.wizardnojs') %}
+{% set ajax_addmore = path('ajax_example.addmore') %}
+{% set ajax_addmore_nojs = path('ajax_example.addmorenojs') %}
+{% set ajax_commands_example = path('ajax_example.advanced') %}
+
+{% 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_checkboxes }}>Generate checkboxes</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_degrade }}>Dependent dropdown degarde</a></p>
+<p><a href={{ ajax_dependent_dropdown_nojs }}>Dependent dropdown degardes w/JS turned off</a></p>
+<p><a href={{ ajax_dynamic_dropdown }}>Dynamic Sections (with graceful degradation)</a></p>
+<p><a href={{ ajax_dynamic_dropdown_nojs }}>Dynamic Sections (with graceful degradation) w/js turned off</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>
+<p><a href={{ ajax_addmore }}>AJAX Add more button</a></p>
+<p><a href={{ ajax_addmore_nojs }}>Add more button (with graceful degradation) w/JS turned off</a></p>
+<p><a href={{ ajax_commands_example }}>AJAX framework commands</a></p>
+{% endtrans %}
diff --git a/examples.module b/examples.module
index 8f732da..8a61378 100644
--- a/examples.module
+++ b/examples.module
@@ -45,6 +45,7 @@ function examples_toolbar() {
     'email_example' => 'email_example.description',
     'events_example' => 'events_example.description',
     'fapi_example' => 'fapi_example.description',
+    'ajax_example' => 'ajax_example.description',
     'field_example' => 'field_example.description',
     'field_permission_example' => 'field_permission_example.description',
     'file_example' => 'file_example.fileapi',
