web-dev-qa-db-fra.com

Comment reconstruire le formulaire après AJAX call

J'essaie de permettre à l'utilisateur de choisir dynamiquement un certain nombre de champs en fonction d'une liste déroulante à l'aide d'un appel ajax, mais je n'arrive pas à obtenir l'appel ajax pour reconstruire le formulaire par la suite.

<?php
class AJAXexample extends BlockBase {
    public function blockForm($form, FormStateInterface $form_state) {
        if (empty($form_state->getValue('number'))) {
            $form_state->setValue('number', 3);
        } 
        $form['columnNum'] = [
            '#title'   => t('Number of Columns'),
            '#type'    => 'select',
            '#options' => [
                1         => '1',
                2         => '2',
                3         => '3',
                4         => '4',
            ],
            '#default_value' => $this->configuration['columnNum'],
            '#empty_option'  => t('-select-'),
            '#ajax'          => [
                'callback'      => [$this, 'columnCallback'],
            ],
        ];
        for ($i = 0; $i < $form_state->getValue('number'); $i += 1) {
            $form['column'][$i] = [
                $i => [
                    '#type'       => 'details',
                    '#title'      => t('Column '.$numTitle),
                    '#open'       => FALSE,
                    'columnTitle' => [
                        '#type'      => 'textfield',
                        '#title'     => t('Column Title'),
                        '#value'     => $config[0]['columnTitle'],
                    ],  
                ],
            ];  
        return $form;
    }

    public function columnCallback(array &$form, FormStateInterface $form_state) {
        $form_state->setValue('number', 10);
        $form_state->setRebuild(true);
        return $form;
    }
}

Le nombre de champs de texte est basé sur la variable form_state 'nombre'. Le callback columnCallback change la variable form_state à 10 et est déclenché lorsque le champ de formulaire 'columnNum' est modifié. Cependant, le formulaire n'est pas reconstruit avec le nouveau nombre de champs même si $ form_state-> setRebuild (); est appelé. Existe-t-il un moyen d'obtenir le formulaire à reconstruire après un appel ajax?

Remarque: j'ai déjà essayé des techniques telles que le remplacement ou l'ajout des éléments du formulaire à l'intérieur de l'appel ajax réel, mais lorsque cela se produit, aucune entrée dans les champs remplacés n'est transmise à $ form_state.

MISE À JOUR: Après avoir essayé la solution de 4k4, je reçois une erreur

Recoverable fatal error: Argument 1 passed to Drupal\Core\Render\MainContent\AjaxRenderer::renderResponse() must be of the type array, null given, called in /Library/WebServer/Documents/aaep/web/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php on line 89 and defined in Drupal\Core\Render\MainContent\AjaxRenderer->renderResponse() (line 45 of /Library/WebServer/Documents/aaep/web/core/lib/Drupal/Core/Render/MainContent/AjaxRenderer.php).

La croyance est que l'erreur se produit car $ form ['colonne'] retourne null malgré sa création en tant que conteneur dans la fonction blockForm. J'ai essayé d'appeler le rappel par d'autres moyens comme

'#ajax' => [
    'callback' => '::columnCallback',
]

et

'#ajax' => [
    'callback' => [$this, '\Drupal\my_examples\Plugin\Block\AJAXexample::columnCallback'],
]

Mais je reçois la même erreur. Curieusement, lorsque je modifie le rappel pour renvoyer l'intégralité de $ form au lieu de $ form ['column'], il répète le formulaire (une copie du formulaire apparaît sous le formulaire actuel) et toujours sans le bon nombre de colonnes.

12
Matt

Le premier problème est de gérer la valeur du numéro de colonne. Lors de la première génération, récupérez-le de la configuration, lors d'une reconstruction, récupérez-le à partir de l'entrée utilisateur et mettez-le dans $columnNum.

La seconde consiste à décider quelle partie du formulaire change dans AJAX et le mettre dans un conteneur div avec l'id columns-wrapper.

class AJAXexample extends BlockBase {
    public function blockForm($form, FormStateInterface $form_state) {
        $columnNum = empty($form_state->getValue('columnNum')) ? $this->configuration['columnNum'] : $form_state->getValue('columnNum');
        $form['columnNum'] = [
            '#title'   => t('Number of Columns'),
            '#type'    => 'select',
            '#options' => [
                1         => '1',
                2         => '2',
                3         => '3',
                4         => '4',
            ],
            '#default_value' => $this->configuration['columnNum'],
            '#empty_option'  => t('-select-'),
            '#ajax'          => [
                'callback'      => [$this, 'columnCallback'],
                'wrapper'       => 'columns-wrapper', 
            ],
        ];
        $form['column'] = [
            '#type' => 'container',
            '#attributes' => ['id' => 'columns-wrapper'],
        ];
        for ($i = 0; $i < $columnNum; $i += 1) {
            $form['column'][$i] = [
                $i => [
                    '#type'       => 'details',
                    '#title'      => t('Column '.$numTitle),
                    '#open'       => FALSE,
                    'columnTitle' => [
                        '#type'      => 'textfield',
                        '#title'     => t('Column Title'),
                        '#value'     => $config[0]['columnTitle'],
                    ],  
                ],
            ];  
        return $form;
    }

Dans le rappel, il suffit de renvoyer le wrapper ajax.

public function columnCallback(array&$form, FormStateInterface $form_state) {
    return $form['column'];
}

Drupal reconstruit le formulaire à chaque demande ajax et le place dans le paramètre $form du rappel. Cela n'aurait aucun sens d'essayer de le reconstruire à nouveau.

4
4k4

Je suppose que vous manquez la méthode wrapper dans votre '#ajax' (à côté de callback) qui consiste en l'attribut HTML id de la zone où le contenu retourné par le rappel doit être placé. Voir: Ajax API . Ensuite, vous devez vous assurer que ce conteneur id existe.

Exemple de code (simplifié):

public function blockForm($form, FormStateInterface $form_state) {
    $form['wrapper'] = array(
        '#type' => 'container',
        '#attributes' => array('id' => 'data-wrapper'),
        );
    $form['wrapper']['columnNum'] = [
        '#title'   => t('Number of Columns'),
        '#type'    => 'select',
        '#options' => [1 => '1', 2 => '2'],
        '#default_value' => $this->configuration['columnNum'],
        '#ajax'          => [
            'callback'   => '::columnCallback',
            'wrapper'    => 'data-wrapper',
        ],
    ];
}
public function columnCallback(array &$form, FormStateInterface $form_state) {
    return $form['wrapper'];
}

Pour un exemple de code complet, voir: Comment ajouter plus d'options pour les radios de type, utilisez Ajax dans Drupal 8 .

2
kenorb