3

I am building an entity reference widget, which displays the reference link, next to the input field, to the target entity's edit form. I make it to open in a modal, with the following attributes on the link:

 'class' => ['use-ajax'],
          'data-dialog-type' => 'modal',
          'data-dialog-options' => json_encode([
            'height' => 700,
            'width' => 900,
          ]),

That works perfectly. However when I click submit on the modal form, I got redirected to the referenced entity view page, instead of the modal being closed and stay on the original form, where this widget is being used.

Is there any special attributes (like "data-dialog-type") which tells to the modal form's submit button to close the modal instead of redirecting to the original view?

UPDATE

Adding this to the URL actually pretty much solves the problem:

    'query' => [
      'destination' => $this->requestStack->getCurrentRequest()->getRequestUri(),
    ],

This redirects the to the original page, with the potential values being updated (referenced item title), which is actually a pretty good thing!

That being said, I am still interested in the "ajax submit" version!

4

You need to implement and use your own EntityEditForm - is this the case ? Then skip to the last code block :D

Otherwise you'll need to accomplish this first. See this answer (Overriding an entity add/edit form through alterroute) for an approach to 'inject' a custom form. Here the crucial code to it (adapted to NodeForm):

modules/custom/form_overwrite/form_overwrite.services.yml

services:
    route_subscriber:
        class: Drupal\form_overwrite\Routing\RouteSubscriber
        tags: 
 - {name: event_subscriber }

modules/custom/form_overwrite/src/Routing/RouteSubscriber.php

 <?php

/**
 * @file
 * Contains \Drupal\form_overwrite\Routing\RouteSubscriber.
 */

namespace Drupal\form_overwrite\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route events.
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
     if ($route = $collection->get('entity.node.edit_form')) {
  $route->setDefault('_entity_form', '\Drupal\form_overwrite\Form\NewEditForm');
     }

   }
}

modules/custom/form_overwrite/src/Form/NewEditForm.php

<?php

namespace Drupal\form_overwrite\Form;

use Drupal\node\NodeForm;


/**
 * Form handler for the node edit forms.
 */
class NewEditForm extends NodeForm {}

There you make sure the #ajax is set, like:

<?php

public function form(array $form, FormStateInterface $form_state) {
   ...
   $form['actions']['DESIRED_EXISTING_ACTION'] = array(
      ...
      '#type' => 'submit',
      '#ajax' => array(
        'callback' => '::ajaxSubmit',
        'event' => 'click',
      ),
      ...
    );
   ...
}

and overwrite the ajaxSubmit function, adding the CloseModalDialogCommand, like:

public function ajaxSubmit(array &$form, FormStateInterface $form_state) {
    $response = new AjaxResponse();

    if ($form_state->getErrors()) {
      ...
    }
    else {
      ...
      $response->addCommand(new \Drupal\Core\Ajax\CloseModalDialogCommand\CloseModalDialogCommand());
    }

    return $response;
  }
  • I want to use general entity forms, like node, and custom forms. Am not able to alter their submit (via hook) to rather make ajax response, since the modal somehow using it's own. Any suggestion on that? – ssibal Apr 26 '17 at 10:59
  • You could fe. alter the route of the entity type your dealing with and set there your 'custom' form. see the newly linked answer. – rémy Apr 26 '17 at 11:02
  • In the link there is a specific entity type. Here this is dynamic, so I won't able to extend from an exact Form class. Probably I will have to use composition here? Could I somehow alter the modal window process, which takes the form submit fields and ajaxifies them? – ssibal Apr 26 '17 at 11:09
  • You'll need to do it for every Form class .. but most of them could be reused, and just the routing part will blow up .. May be a better way thoe ... – rémy Apr 26 '17 at 11:20
  • Well, I don't want to write a code, which will need to be extended each time there is a new content entity type: \ – ssibal Apr 26 '17 at 11:27
2

I solved this by only using a event subscriber in a custom module.

modalCloseSubscriber.php

<?php

namespace Drupal\custom_module\EventSubscriber;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * Custom module event subscriber.
 */
class ModalCloseSubscriber implements EventSubscriberInterface {

  /**
   * Current logged in user.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * Constructs event subscriber.
   *
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   Current logged in user.
   */
  public function __construct(AccountProxyInterface $current_user) {
    $this->currentUser = $current_user;
  }

  /**
   * Kernel response event handler.
   *
   * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
   *   Response event.
   */
  public function onKernelResponse(FilterResponseEvent $event) {
    $response = $event->getResponse();
    if (!$response instanceof AjaxResponse) {
      return;
    }
    if ($response->getCommands()) {
      $commands = $response->getCommands();
      if (empty($commands)) {
        return;
      }
      elseif (!empty($commands)) {
        foreach ($commands as $command) {
          if ($command['command'] == 'insert' && $command['selector'] == '#layout-builder') {
            // leave blank for default modal window to close.
            $response->addCommand(new CloseDialogCommand());
          }
        }
      }
    }

  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      KernelEvents::RESPONSE => ['onKernelResponse'],
    ];
  }

}

This closes the modal after i create a block with layout builder.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.