0

This question is asked few times but I am still lost on my solution. I did check this but couldn't make my code work.

My my_module.routing.yml

entity.node.my_module_form.node:
  path: '/contribute/{node}/form'
  defaults:
    _controller: '\Drupal\my_module\Controller\Controller::getAjaxFormForNode'
    _title_callback: ''
  cache_tags:
    - my_module_action
  requirements:
    _user_is_logged_in: 'TRUE'

entity.node.my_module_contribute_form:
  path: '/contribute/add'
  defaults:
    _controller: '\Drupal\my_module\Controller\Controller::action'
    _title: 'Translate'
  cache_tags:
    - my_module_action
  requirements:
    _user_is_logged_in: 'TRUE'

My Drupal\my_module\Controller\Controller.php

<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\node\NodeInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Access\AccessResult;
use Symfony\Component\HttpFoundation\Response;

/**
 * Controller for our dynamic tab.
 */
Class Controller extends ControllerBase
{


  public function action(RouteMatchInterface $route_match)
  {
    $entityTypeManager = \Drupal::entityTypeManager();

    $nodes = [];
    $query = \Drupal::entityQuery('node');
    //TODO get the type from url
    $query->condition('type', 'article');

    //$query->condition($field, $condValue, $operator);
    $group = $query->orConditionGroup()
      ->condition('status', 0)
      //->condition('field_popularity', 100, ">")
    ;
    $query
      ->condition($group)
    ;

    //give them only 10 items
    $query->range(0, 2);

    $nodeIds = $query->execute();
    if (!empty($nodeIds)) {
      try {
        $nodes = $entityTypeManager->getStorage('node')->loadMultiple($nodeIds);
      } catch (\Exception $e) {

      }
    }

    //var_dump($nodeIds); die();
    $build = [
      '#theme' => 'my_add',
      '#nodes' => $nodes,
      '#node_ids' => implode(",", $nodeIds),
    ];


    return $build;
  }

  // Controller to load the form through ajax
  public function getAjaxFormForNode($node) {
    if (!$node instanceof NodeInterface) {
      $nodeId = $node;
      $node = \Drupal::entityTypeManager()->getStorage('node')->load($nodeId);
    }
    $form = \Drupal::formBuilder()->getForm('Drupal\my_module\Form\Form', ['node' => $node->id()]);
    return $form;
    $ajax_response = new AjaxResponse();

    // Append the form in the page.
    // The form is sent as is, not rendered
    //$ajax_response->addCommand(new AppendCommand('meaning-add', $form));

    // Add an AJAX command to open a modal dialog with the form as the content.
    $response->addCommand(new OpenModalDialogCommand('My Modal Form', $form, ['width' => '800']));

    //var_dump($ajax_response); die();

    return $ajax_response;
  }
}

And my

<?php

namespace Drupal\my_module\Form;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityStorageBase;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\my_module\Entity\myEntity;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\user\EntityOwnerInterface;

/**
 * Class Form.
 */
Class Form extends FormBase {

  /**
   * Drupal\Core\Entity\EntityTypeManagerInterface definition.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Constructs a new Form.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'my_module_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $nid = null) {
    if ($nid === null) {
      $nid = \Drupal::routeMatch()->getParameter('node');
    }

    // Prevent page reload when user hits enter key in the searchbox.
    // If I remove this then the form will submit 
    // but will redirect to "/contribute/{node}/form"
    $form['#attributes'] = [
      'onsubmit' => 'return false',
    ];

    $form['#prefix'] = '<div id="modal_example_form">';
    $form['#suffix'] = '</div>';

    $user = \Drupal::currentUser();
    $userId = $user->id();
    $myEntity =  \Drupal::entityTypeManager()->getStorage('my_module')
      ->loadByProperties(['node_id' => $nid, 'user_id' => $userId])
    ;
    /** @var myEntity $myEntity */
    $myEntity = reset($myEntity);
    $challengeId = $vote = $meaning = $challenge = null;
    if ($myEntity) {
      $meaning = $myEntity->getMeaning();

      $form['id'] = [
        '#type' => 'hidden',
        '#value' => $myEntity->id(),
      ];
    }
    $form['nid'] = [
      '#type' => 'hidden',
      '#value' => $nid,
    ];

// The status messages that will contain any form errors.
    $form['status_messages'] = [
      '#type' => 'status_messages',
      '#weight' => -10,
    ];
    $form['meaning'] = [
      '#type' => 'textfield',
      '#title' => $this->t(''),
      //'#description' => $this->t('Enter the text for the term.'),
      '#placeholder' => $this->t('Translation'),
      '#maxlength' => 64,
      '#size' => 64,
      '#weight' => '10',
      '#required' => FALSE,
      '#default_value' => $meaning,
      '#ajaxs' => [
        'callback' => '::submitAjaxForm', // don't forget :: when calling a class method.
        'disable-refocus' => TRUE, // Or TRUE to prevent re-focusing on the triggering element.
        'event' => 'change',
        'wrapper' => 'edit-output', // This element is updated with this AJAX callback.
        'progress' => [
          'type' => 'throbber',
          'message' => $this->t('Verifying entry...'),
        ],
      ]

    ];

    $form['actions'] = array('#type' => 'actions');
    $form['actions']['send'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit modal form'),
      '#attributes' => [
        'class' => [
          'use-ajax',
        ],
      ],
      '#ajax' => [
        'callback' => [$this, 'submitAjaxForm'],
        'event' => 'click',
      ],
    ];


    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';

    return $form;
  }

  // Get the value from example select field and fill
// the textbox with the selected text.
  public function submitAjaxForm(array &$form, FormStateInterface $form_state)
  {
    $form['color_wrapper']['#value'] = 'done';
    //return $form['color_wrapper'];
    //$this->submitForm($form, $form_state);
    $response = new AjaxResponse();
    // If there are any form errors, re-display the form.
    if ($form_state->hasAnyErrors()) {
      $response->addCommand(new ReplaceCommand('#my_module_form', $form));
    }
    else {
      $response->addCommand(new OpenModalDialogCommand("Success!", 'The modal form has been submitted.', ['width' => 800]));
    }
    return $response;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state)
  {
  }

  /**
   * Gets the configuration names that will be editable.
   *
   * @return array
   *   An array of configuration object names that are editable if called in
   *   conjunction with the trait's config() method.
   */
  protected function getEditableConfigNames() {
    return ['config.my_module_form'];
  }

}

My my-add.html.twig

<div class="meaning-contribute-form" data-nodes="{{ node_ids }}">
  {% for index, node in nodes %}
    {% set title = node.getTitle()|lower|capitalize %}
    <div>{{ title }}</div>

    <div class="meaning-add-wrapper meaning-{{ node.id() }}" data-id="{{ node.id() }}">
    </div>
    <hr>
  {% endfor %}
</div>

And my js:

(function ($, Drupal, drupalSettings) {

  'use strict';
  function loadForm(div, settings = null)
  {
    let nodeId = div.attr('data-id');
    //Find all our fields with autocomplete settings.
    var typeEndpoint = Drupal.url('contribute/' + nodeId + '/form');

    div.load(typeEndpoint + " #my-module-form");
  }
  Drupal.behaviors.my_module_add = {
    attach: function (context, settings) {
      var wrapper = $(".meaning-add-wrapper");
      var nodeIds = $(".meaning-contribute-form").attr('data-nodes');
      nodeIds = nodeIds.split(",");
      wrapper.once('my_module').each(function () {
        //console.log($(this));
        let _this = $(this);
        loadForm(_this, settings);
        Drupal.attachBehaviors();
      });
    }
  };
})(jQuery, Drupal);

The form is loading via AJAX correctly but the submit is not working as expected.

What I want

Load the form via AJAX and submit the form via AJAX.

The problem

When I go to /contribute/{node}/form (node = nodeId), the ajax form submit is working. However, when I go to /contribute/add then the ajax submit is not working. When the below code is added then it doesn't do anything and when removed then it submits and also takes to /contribute/{node}/form page, which I don't want.

$form['#attributes'] = [
  'onsubmit' => 'return false',
];

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.