it-swarm.com.de

Wie speichere ich Knotenformulardaten zwischen Formularschritten mit Ajax?

Ich möchte ein Knotenformular mit Ajax in ein mehrstufiges Formular verwandeln. Ich habe ajax-fähige Senden-Schaltflächen und eine Logik in einem hook_form_alter (Siehe unten) hinzugefügt, um In- und Dekrementierungsschritte auszuführen und Formularelemente basierend auf dem aktuellen Schritt anzuzeigen und auszublenden. Das Hin- und Hergehen funktioniert hervorragend, um die richtigen Elemente wiederzugeben.

Das Problem ist, wenn ich einige Daten in einem Schritt eingebe, vorwärts oder rückwärts gehe und dann zum ursprünglichen Schritt zurückkehre, werden die von mir eingegebenen Daten aus dem Formular entfernt. Wenn ich den Wert des Elements, für das ich Daten eingegeben habe, mit FormStateInterface::getValue() überprüfe, sind die Daten vorhanden, wenn mein Änderungs-Hook beim Verlassen des ursprünglichen Schritts ausgeführt wird, aber bei nachfolgenden Next/Back-Übergängen ausgeführt wird. Dieses Problem gilt auch nicht für Daten, die sich bereits im Formular befinden (z. B. beim Bearbeiten eines vorhandenen Knotens).

Natürlich muss ich die eingehenden Werte irgendwo speichern, aber wo? Und wie stelle ich sie dann im entsprechenden Schritt in den Formularelementen wieder her?

Mein Code (minimales Beispiel):

<?php
/**
 * Implements hook_form_FORM_ID_alter().
 */
function my_module_form_node_ajax_test_form_alter(&$form, $form_state) {
  // Define steps. Each child array is a step, and each string in the
  // a child array is a field that should be present on that step.
  $steps = [
    ['title'],
    ['body'],
  ];
  // Add wrapper.
  $form['#prefix'] = '<div id="ajax-form-wrapper">';
  $form['#suffix'] = '</div>';
  // If the step is not set, this is step 0.
  if (!$form_state->has('step')) {
    $form_state->set('step', 0);
  }
  // Get the current step.
  $step = $form_state->get('step');
  // If we have a triggering element, increment or decrement step as
  // appropriate.
  if ($trigger = $form_state->getTriggeringElement()) {
    switch ($trigger['#name']) {
      case 'next':
        $form_state->set('step', ++$step);
        break;

      case 'back':
        $form_state->set('step', --$step);
        break;
    }
  }
  // Show elements in the current step, hide others. Always show the
  // `actions` element, and all `form_*` elements.      
  foreach (Element::children($form) as $element) {
    $form[$element]['#access'] = (
      in_array($element, $steps[$step])
      || $element == 'actions'
      || strpos($element, 'form') === 0
    );
  }
  // Show the back button if this is not the first step.
  $form['actions']['back'] = $step > 0 ? ajax_button('back') : NULL;
  // Show the next button if this is not the last step.
  $is_last_step = $step == count($steps) - 1;
  $form['actions']['next'] = !$is_last_step ? ajax_button('next') : NULL;
  // Show the submit button if this is the last step.
  $form['actions']['submit']['#access'] = $is_last_step;
}

/**
 * Return a render array for an ajax step button.
 */
function ajax_button($name) {
  return [
    '#type' => 'button',
    '#value' => $name,
    '#name' => $name,
    '#ajax' => [
      'wrapper' => 'ajax-form-wrapper',
    ],
  ];
}
5
Xaq

Nun, ich habe es herausgefunden. (Dies war vor der Antwort von @ 4k4, was sehr nahe an dem liegt, was ich am Ende hatte).

Ich musste die Entität mithilfe der übermittelten Formularwerte erstellen und dann das Formular so einstellen, dass es in einem Übermittlungshandler neu erstellt wird (wodurch die Werte der Entität in das Formular eingefügt werden). So habe ich es gemacht:

<?php
/**
 * Return a render array for an ajax step button.
 */
function ajax_button($name) {
  return [
    // Changed from 'button', buttons don't invoke submit handlers, 
    // submits do.
    '#type' => 'submit',
    '#value' => $name,
    '#name' => $name,
    // Set the submit handler.
    '#submit' = ['my_module_ajax_form_submit'],
    '#ajax' => [
      'wrapper' => 'ajax-form-wrapper',
    ],
  ];
}

/**
 * Build entity using submitted form data and rebuild form.
 */
function my_module_ajax_form_submit($form, $form_state) {
  // Get the NodeForm object.
  $form_object = $form_state->getFormObject();
  // NodeForm::buildEntity() maps form values into the entity object
  // and returns it.
  $entity = $form_object->buildEntity($form, $form_state);
  // NodeForm::setEntity() stores the entity in the form object which 
  // is used to populate form fields when the form rebuilds.
  $form_object->setEntity($entity);
  // Set the form to rebuild.
  $form_state->setRebuild()
}

Ich bin hierher gekommen, indem ich die verschiedenen Implementierungen von ::buildEntity() und ::buildForm() in NodeForm und seinen Vorfahren ContentEntityForm und EntityForm .

1
Xaq

Dieser Code ist ziemlich beeindruckend. Ich dachte nicht, dass es möglich wäre, ein mehrstufiges Entitätsformular mit ein paar Zeilen in einem Form-Alter-Hook zu erstellen. Um dies so einfach zu halten, können Sie versuchen, das Entitätsobjekt zum Speichern der Werte zu verwenden, da das Formularobjekt zwischen den Schritten serialisiert und zwischengespeichert wird und eine Methode zum Erstellen einer solchen Entität aus den übermittelten Werten Teil der Entitätsformularklasse ist:

  if ($trigger = $form_state->getTriggeringElement()) {
    $form_object = $form_state->getFormObject();
    $new_entity = $form_object->buildEntity($form, $form_state);
    $form_object->setEntity($new_entity);
3
4k4

Ich denke, Sie müssen die Symfony-Sitzungen verwenden

Sitzungsvariable festlegen

$session = \Drupal::request()->getSession();
$session->set('mymodule_variable', 'value');

Später können Sie diese Variable so erhalten

$session = \Drupal::request()->getSession();
$value = $session->get('mymodule_variable', 'default_value');

Symfony-Sitzungen entsprechen php $ _SESSION

0
GiorgosK

Es scheint, dass der richtige Weg, dies zu tun, mit PrivateTempStore ist. Wenn Sie das CommentAdminOverview in Core sehen, sehen Sie Folgendes:

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
      ...
      $info = [];
      /** @var \Drupal\comment\CommentInterface $comment */
      foreach ($comments as $comment) {
        $langcode = $comment->language()->getId();
        $info[$comment->id()][$langcode] = $langcode;
      }
      $this->tempStoreFactory
        ->get('comment_multiple_delete_confirm')
        ->set($this->currentUser()->id(), $info);
      $form_state->setRedirect('comment.multiple_delete_confirm');
    }
  }

Auf dieser Seite sammeln Sie die Kommentar-ID, die Sie löschen möchten, und führen eine Umleitung durch. Das Löschen des Kommentars erfolgt in comment.multiple_delete_confirm ( ConfirmDeleteMultiple ) mit folgendem Code:

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $this->commentInfo = $this->tempStoreFactory->get('comment_multiple_delete_confirm')->get($this->currentUser()->id());
    if (empty($this->commentInfo)) {
      return $this->redirect('comment.admin');
    }
    /** @var \Drupal\comment\CommentInterface[] $comments */
    $comments = $this->commentStorage->loadMultiple(array_keys($this->commentInfo));
  ...
  }

Ansichten funktionieren auch mit PrivateTempStorage. Wenn Sie zu einer Ansicht zurückkehren, die nicht gespeichert ist, können Sie die Änderungen erneut anzeigen.

0