How to add new form field dynamically on selected change using ajax I'm trying to make dynamic form in my module:
I have one field of type select containing some options, and when selecting one option a new field should be added dynamically to the form which in turn will contain another list of options.
here what I try to do but nothing seems to work correctly

public function buildForm(array $form, FormStateInterface $form_state) {
    $this-&gt;controller = new Place_AdsController();
    $terms = $this-&gt;controller-&gt;content();

    $form['#attached']['library'][] = 'Place_Ads/general';
    $form['level1'] = array(
        '#type' =&gt; 'select',
        '#description' =&gt; 'Please select level one term',
        '#options' =&gt; $terms,
        '#attributes' =&gt; array(
            'class' =&gt; array(
                'mycategory'
            ),
        ),
        '#ajax' =&gt; array(
            // Function to call when event on form element triggered.
            'callback' =&gt; 'Drupal\Place_Ads\Form\Place_AdsForm::level1SelectedCallback',
            // Effect when replacing content. Options: 'none' (default), 'slide', 'fade'.
            'effect' =&gt; 'fade',
            // Javascript event to trigger Ajax. Currently for: 'onchange'.
            'event' =&gt; 'change',
            'progress' =&gt; array(
                // Graphic shown to indicate ajax. Options: 'throbber' (default), 'bar'.
                'type' =&gt; 'throbber',
                // Message to show along progress graphic. Default: 'Please wait...'.
                'message' =&gt; NULL,
            ),
        ),
    );
    return $form;
}
public function level1SelectedCallback(array &amp;$form, FormStateInterface $form_state) {
    // Instantiate an AjaxResponse Object to return.
    $ajax_response = new AjaxResponse();
    if ($form_state-&gt;getValue('level1') != false) {
        $form['level2'] = array(
            '#type' =&gt; 'select',
            '#description' =&gt; 'Please select level two term',
            //'#options' =&gt; $terms,
            '#attributes' =&gt; array(
                'class' =&gt; array(
                    'mycategory'
                ),
            ),
        );
        $form_state-&gt;setRebuild(TRUE);
    }

    return $ajax_response;
}
Build form elements in buildForm() only, not in ajax callback. The ajax callback is only for preparing the ajax response. When the ajax callback is called, buildForm() has already run with the updated $form.

Your ajax response is empty. You didn't add any ajax command to it. See ajax examples how you can do this.

But in your case you don't need any code in ajax callback. It would be enough to simply return the changed part of $form in a div container.

For this put an empty container in buildForm() and move the select form element from the ajax callback to buildForm() and put it in the container, if the condition in $form_state is met:

$form['ajax_wrapper'] = [
  '#type' =&gt; 'container',
  '#attributes' =&gt; ['id' =&gt; 'ajax-wrapper'],
];

if ($form_state-&gt;getValue('level1') != false) {
        $form['ajax_wrapper']['level2'] = array(
            '#type' =&gt; 'select',
            '#description' =&gt; 'Please select level two term',
            //'#options' =&gt; $terms,
            '#attributes' =&gt; array(
                'class' =&gt; array(
                    'mycategory'
                ),
            ),
        );


And the ajax callback:

public function ajaxCallback(array &amp;$form, FormStateInterface $form_state) {
  return $form['ajax_wrapper'];


change #ajax to:

'#ajax' =&gt; [
  'callback' =&gt; '::ajaxCallback',
  'wrapper' =&gt; 'ajax-wrapper',
],