6

I want to render two views in the same twig template, but only one of them is rendered, and the second is always override with the same content as the first one.
I have a content type 'contacts' and when its nodes are displayed (full view) I want to render two views in the same page after the node fields. I use a twig template node--contacts.html.twig :

.....  node fields twig template .... 
{{ viewCalls }}  
....  
{{ viewAppointments }}  
....

And my function my_theme_preprocess_node(&$variables) in my .theme file:

....  
$view = \Drupal\views\Views::getView('view_calls');
$view->setDisplay('default');
$view->setArguments(array($variables['node']->id()));
$view->setOffset(0);
$view->execute();
$variables['viewCalls'] = $view->render();

$view2 = \Drupal\views\Views::getView('view_appointments');
$view2->setDisplay('default');
$view2->setArguments(array($variables['node']->id()));
$view2->setOffset(0);
$view2->execute();
$variables['viewAppointments'] = $view2->render();
.....

As I said, the second view always shows the same content as the first. If I reverse the order of rendering, then the content is reverse too, and the one which is first rendered is the only one I see, and always two times. It is as {{ viewCalls }} and {{ viewAppointments }} always get the same content.

3 Answers 3

10

The reason you get the same content is due to the #cache property of the render array you get when you call the render() method.

Most specifically, by calling setOffset() earlier in your code, you are implicitly setting a cache key on the render array, (see here):

$this->element['#cache']['keys'][] = 'offset:' . $offset;

This cache key setting, in combination with the #theme property of the render array (views_view), which is the same for both views, will result in getting a cached version of the first one when including the second one, since the [#theme] + [#cache][keys] combination is identical for both views.

To work around that you could:

  1. Remove caching completely for these views, or
  2. Avoid using setOffset(), or
  3. Add more specific keys to the cache so that you have a different cache for each view. E.g.

    $variables['viewCalls']['#cache']['keys'] = 
      array_merge(
        $variables['viewCalls']['#cache']['keys'], 
        array('viewCalls')
      );
    ...
    $variables['viewAppointments']['#cache']['keys'] = 
      array_merge(
        $variables['viewAppointments']['#cache']['keys'],
        array('viewAppointments')
      );
    

I tested your case using the 3. option and it worked as expected, I think before resolving to using another module, you could try sticking to your original code and appropriately modifying the #cache property of both render arrays.

Good luck!

6
  • 1
    Great answer. I think the problem is that render() is not officially part of the Views API. Normally you should build a render element '#type' => 'view', see Programatically render a view in twig template. Although I don't know how you can implement a custom offset dynamically through the Views API (if this is what the question is about, setting an offset other than 0).
    – 4uk4
    Commented Oct 29, 2018 at 7:49
  • Thanks very much. I works good. I have removed caching in my two views and setOffset and now two views are rendered right.
    – briast
    Commented Oct 29, 2018 at 7:52
  • 2
    @briast – To remove caching actually is just some lukewarm medicine, it's not a fix to the underlying problem. Why would you remove caching and provide worse UX due to the resulting slower page speed when you could add your own cache keys as this brilliant in-depth answer shows you?
    – leymannx
    Commented Oct 29, 2018 at 9:29
  • 2
    @briast, if you don't need setOffset() (which causes together with the unsupported render() this issue), simply render a view with: ['#type' => 'view', '#name' => $name, '#display_id' => $display_id, '#arguments' => $args]
    – 4uk4
    Commented Oct 29, 2018 at 9:55
  • 1
    @leymannx - You're right. I've tried with the third options and it worked good. Thanks for your suggestion.
    – briast
    Commented Oct 29, 2018 at 10:42
4

Render two views in the same Twig template with the render element view:

$variables['viewCalls'] = [
  '#type' => 'view',
  '#name' => 'view_calls',
  '#display_id' => 'default',
  '#arguments' => [$variables['node']->id()],
];

$variables['viewAppointments'] = [
  '#type' => 'view',
  '#name' => 'view_appointments',
  '#display_id' => 'default',
  '#arguments' => [$variables['node']->id()],
];
2
  • Thanks. I didn't know a view could be render this way. I have changed it and it works good.
    – briast
    Commented Oct 29, 2018 at 12:59
  • @briast Yes, this is an option too, doesn't allow you to control the offset though. It get's even easier with views_embed_view(), see computerminds.co.uk/articles/… Commented Oct 29, 2018 at 21:33
3

There's at least one typo in your code. Remove the space in $variables['viewAppointments '].


Alternatively you could also use Twig Tweak. Which will provide you a function you can use to place views directly in your templates.

{{ drupal_view('who_s_new', 'block_1', arg_1, arg_2, arg_3) }}`

Twig Tweak is a small module which provides a Twig extension with some useful functions and filters that can improve development experience.

Read the full guide on Twig Tweak and Views.

Twig Tweak's drupal_view() method provide's access to embed views within any Twig code, including dynamically from within each row of another view. This feature provides an alternate method to accomplish the nesting provided by the Views field view module.

2
  • 1
    Thanks for your help. Yes, there was a typo but It was only in my answer. My code is right. Before posting my answer I read about the module you have mentioned. A quick review of this module didn't let me see how to pass arguments to the views, but I'll try it more in depth. Any way, I'm wondering why my code doesn't work and duplicate the view. It's a mystery.
    – briast
    Commented Oct 28, 2018 at 21:42
  • 1
    @briast {{ drupal_view('view_calls', 'default', node.id) }}
    – No Sssweat
    Commented Oct 28, 2018 at 22:14

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.