Comment reconstruire un formulaire après un appel AJAX

12

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.

Mat
la source
Peut-être une faute de frappe mais une double vérification, savez-vous que dans columnCallback, le premier argument est une faute de frappe (pas d'espace entre le tableau et & $ form)?
Kevin

Réponses:

4

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

La seconde est de décider quelle partie du formulaire change dans AJAX et de 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 $formdu rappel. Cela n'aurait aucun sens d'essayer de le reconstruire à nouveau.

4k4
la source
1
Je reçois une erreur après l'appel de la demande ajax. 'Code de résultat HTTP: 200' StatusText: OK ResponseText:
Matt
1
Les choses de test, j'ai effectué un dé (print_r ($ form_state-> getValues ​​())); et il a correctement affiché la valeur columnNum appropriée. Il ne fait qu'errer autrement.
Matt
1
J'ai mis les changements dans votre code pour démonstration. Je ne peux pas aider au débogage sans messages d'erreur avec des numéros de ligne.
4k4
2
Avez-vous supprimé l'erreur de syntaxe du commentaire de @ Kevin? Y a-t-il des erreurs php dans le journal des erreurs? Il devrait y en avoir beaucoup lors du test de nouveau code comme celui-ci.
4k4
2
Tracé de l'erreur, cela signifie qu'il return $form['column']est nul, car la valeur de retour n'est pas cochée renderResponse(). Cela pourrait toujours être un problème avec la liste des paramètres du rappel, car nous avons mis au moins un conteneur dans cette clé de formulaire et cela empêcherait cette erreur.
4k4
2

Je suppose que vous manquez de wrapperméthode dans votre '#ajax'(à côté de callback) qui consiste en l' idattribut HTML de la zone où le contenu renvoyé par le rappel doit être placé. Voir: API Ajax . Ensuite, vous devez vous assurer qu'un tel conteneur idexiste.

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 utiliser Ajax dans Drupal 8 .

Kenorb
la source