Cómo reconstruir el formulario después de una llamada AJAX

12

Estoy tratando de permitir que el usuario elija dinámicamente una cantidad de campos en función de un cuadro desplegable utilizando una llamada ajax, pero parece que no puedo obtener la llamada ajax para reconstruir el formulario después.

<?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;
    }
}

El número de campos de texto se basa en la variable form_state 'número'. La devolución de llamada columnCallback cambia la variable form_state a 10 y se activa cuando se cambia el campo de formulario 'columnNum'. Sin embargo, el formulario no se reconstruye con el nuevo número de campos aunque $ form_state-> setRebuild (); se llama. ¿Hay alguna manera de hacer que el formulario se reconstruya después de una llamada ajax?

NOTA: Ya he probado técnicas como reemplazar o agregar los elementos del formulario dentro de la llamada ajax real, pero cuando eso sucede, ninguna de las entradas en los campos reemplazados se pasa a $ form_state.

ACTUALIZACIÓN: después de intentar la solución de 4k4, recibo un error

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 creencia es que el error ocurre porque $ form ['column'] está volviendo nulo a pesar de haber sido creado como un contenedor en la función blockForm. He intentado llamar a la devolución de llamada de otras maneras como

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

y

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

Pero recibo el mismo error. Curiosamente, cuando cambio la devolución de llamada para devolver todo el formulario $ en lugar de solo $ formulario ['columna'], repite el formulario (una copia del formulario aparece debajo del formulario actual) y aún sin el número adecuado de columnas.

Mate
fuente
Puede ser un error tipográfico pero una doble comprobación, ¿sabe que en columnCallback el primer argumento es un error tipográfico (sin espacio entre la matriz y & $ form)?
Kevin

Respuestas:

4

El primer problema es manejar el valor para el número de columna. En la primera compilación, obténgalo desde la configuración, en una reconstrucción, consígalo desde la entrada del usuario y colóquelo $columnNum.

El segundo es decidir qué parte del formulario cambia en AJAX y poner esto en un contenedor div con la identificación 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;
    }

En la devolución de llamada solo necesitamos devolver el contenedor ajax.

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

Drupal reconstruye el formulario en cada solicitud ajax y lo coloca en el parámetro $formde la devolución de llamada. No tendría sentido tratar de reconstruirlo nuevamente.

4k4
fuente
1
Recibo un error después de que se llama la solicitud ajax. 'Código de resultado HTTP: 200' Texto de estado: OK Texto de respuesta:
Matt
1
Las cosas de prueba que realicé un dado (print_r ($ form_state-> getValues ​​())); y mostró correctamente el valor correcto de columnNum. Simplemente errores de lo contrario.
Matt
1
He puesto los cambios en su código para demostración. No se puede ayudar con la depuración sin mensajes de error con números de línea.
4k4
2
¿Eliminó el error de sintaxis del comentario de @ Kevin? ¿Hay errores de php en el registro de errores? Debería haber mucho al probar un código nuevo como este.
4k4
2
Rastreó el error, significa que return $form['column']es nulo, porque el valor de retorno se desmarca renderResponse(). Todavía podría ser un problema con la lista de parámetros de la devolución de llamada, porque ponemos al menos un contenedor en esa clave de formulario y esto evitaría este error.
4k4
2

Supongo que te falta un wrappermétodo en tu '#ajax'(junto a callback) que consiste en el idatributo HTML del área donde se debe colocar el contenido devuelto por la devolución de llamada. Ver: API de Ajax . Entonces debes asegurarte de que idexista dicho contenedor .

Ejemplo de código (simplificado):

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'];
}

Para ver un ejemplo de código completo, consulte: Cómo agregar más opciones para radios de tipo use Ajax en Drupal 8 .

kenorb
fuente