5

What ist the correct way to load a View via Ajax and not breaking the view ajax functionality like the pager?

I tried it this way:

In a template:

<a href="/example-module/15?token=ugi2XH02Cumb3lsbSrOutwzRYSMXmn_bRKJI3FQ-hhk" class="btn use-ajax" role="button" rel="nofollow">
  {{- 'Load View' -}}
</a>

In the routing file:

example_module.example:
  path: '/example-module/{node_entity}'
defaults:
  _controller: '\Drupal\example_module\Controller\LoadViewController::render'
  _title: 'Load View'
options:
  parameters:
    node_entity:
      type: entity:node
requirements:
  _permission: 'access content'
  _csrf_token: 'TRUE'

In the Controller:

public function render(NodeInterface $node_entity, Request $request) {
  $node_id = $node_entity->id();
  if ($request->get(MainContentViewSubscriber::WRAPPER_FORMAT) == 'drupal_ajax') {
    $views_block = views_embed_view('load_view', 'block_1', $node_id);

    $response = new AjaxResponse();
    $selector = '#example-id';
    $replace = new ReplaceCommand($selector, \Drupal::service('renderer')->render($views_block));
    $response->addCommand($replace);
    return $response;
  }
  else {
    // For entities that don't have a canonical URL (like paragraphs),
    // redirect to the front page.
    $front = Url::fromUri('internal:/');
    $response = new RedirectResponse($front);
  }
  return $response;
}

This code works well. With two exceptions:

  • The view is not working with ajax (e.g. Pager). But the option "use AJAX" is checked in the UI --- I also tried it with the Views::getView() method and add explicit the $view->setAjaxEnabled(TRUE) option. No success.
  • The Pager of the View has the URL of the controller in it, not the URL from the current rendered page.

So if you click on the Pager of the new loaded Views, the page reloads and show you on a white screen the Ajax command, but no specific error, not in the console and not in the logs.

Is this way not working or what I'm doing wrong?

4
  • Views Ajax uses its own endpoint. It's hard to mix custom Ajax with Views Ajax. I would choose one of them. If you go with Views Ajax then deliver a basic version of the View and hide it in js until the user clicks a button. If you go with your custom Ajax then disable the Views Ajax and handle the incoming pager request in the controller. In this case you have to add use-ajax to the pager links as well, so that the returned ajax response is handled by the client.
    – 4uk4
    Commented Oct 21, 2019 at 7:59
  • Thank you very much 4k4. The first option is not a good one in my situation, because that would mean that I have to load multiple views on the same page and maybe don't need them if the User don't click at the Load-Button. The second option is better in this context (perfomance), but that can mean a lot of work if you wanna go a step further and use exposed filter in the loaded view etc. So therefore option one is still the better one... And there is no other solution, maybe an option three? Commented Oct 21, 2019 at 9:52
  • OK, I think it's clear what you are asking and we'll see if someone can provide an answer. By the way, I don't think there is a performance issue with the first option, because the initially rendered Views are cached the same way as any other rendered output.
    – 4uk4
    Commented Oct 21, 2019 at 12:36
  • Another idea, is it possible to load the Block of the View and passing some arguments to the View instead load the View directly? Would that change anything? Commented Oct 21, 2019 at 14:47

1 Answer 1

6

After examining the ViewAjaxController.php ajaxView() function, I came up with the following solution, which seems functioning for me. I created an ajax endpoint which extends ViewAjaxController and sets the requested view and display in the Request object then calls the parent ajaxView() method.

1. Define the route

my_module.routing.yml

my_module.views_ajax:
  path: '/admin/views/{nojs}/{view_id}/{display_id}/{args}'
  defaults:
    _controller: '\Drupal\my_module\Controller\MyModuleViewAjaxController::ajaxView'
  requirements:
    _permission: 'access content'

2. Create the controller

src/Controller/MyModuleViewAjaxController.php

namespace Drupal\my_module\Controller;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\views\Controller\ViewAjaxController;
use Symfony\Component\HttpFoundation\Request;

/**
 * Class MyModuleViewAjaxController
 */
class MyModuleViewAjaxController extends ViewAjaxController {

  public function ajaxView(Request $request) {
    $view_name = $request->get('view_id');
    $display_id = $request->get('display_id');
    $dom_id = "{$view_name}__{$display_id}";

    $request->request->set('view_name', $view_name);
    $request->request->set('view_display_id', $display_id);
    $request->request->set('view_args', $request->get('args'));
    $request->request->set('view_dom_id', $dom_id);

    return parent::ajaxView($request);
  }

}

3. Create a wrapper <div> to embed the view

The <div> where you want to load a view via ajax has to have a specific css class in form of: .js-view-dom-id-{view_id}__{display_id}. Here replace the placeholders for view and display ids with the actual ones you want to embed.

<div class="js-view-dom-id-{view_id}__{display_id}"></div>

4. Load the view via ajax link

Now you can load the view creating links like:

<a href="/admin/views/ajax/{view_id}/{display_id}/{args}" class="use-ajax">
    This link will load the view {view_id} display {display_id} with the
    arguments {args}
</a>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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