Coming to this while testing #1898474: pager.inc - Convert theme_ functions to Twig.

If a pager section is meant to be shown in the view preview, the view is not shown in the preview UI, and an error is logged in watchdog:

Twig_Error_Runtime: An exception has been thrown during the rendering of a template ("Passed variable is not an array or object, using empty array instead") in "core/modules/views/templates/views-view.html.twig" at line 73. in Twig_Template->displayWithErrorHandling() (line 279 of /home/s86294e944c6f7fd/www/core/vendor/twig/twig/lib/Twig/Template.php).

This seems related to the fact that the Twig template is expecting a rendered array, while the view pager plugins are still pre-rendering the HTML.

Steps to reproduce:

  • create 3 nodes
  • promote them to front page
  • change the pager setup of the Frontpage view to display 2 items per page
  • save the view
  • no output in the preview section
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mondrake’s picture

Status: Active » Needs review
FileSize
1.67 KB

This patch changes output of the pager plugins from rendered HTML to rendered arrays.

Note: even if the pager is shown as a result of this patch, navigation between different pages is not working. An issue exists for that, see #1904922: Views UI Preview - pager navigation is broken

mondrake’s picture

mondrake’s picture

#1: 2030293-1.patch queued for re-testing.

R.Hendel’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
66.88 KB
83 KB

The patch works very well.

For testing I installed an new empty drupal 8.x and could reproduce the error very easy by generating 50 items with devel and creating a views using a pager with 10 items at each site.

Before patching:
There are no items displayed in preview:

2030293_before.png

After patching:
After applying patch and clearing cache same view displayes items im preview as expected. The pager itself is also being rendered (out of screenshot...)

2030293_after.png

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

Considering a php error is demonstrable we have tests to ensure we don't break this in the future

+++ b/core/modules/views/lib/Drupal/views/Plugin/views/pager/Full.phpundefined
@@ -89,12 +89,13 @@ function render($input) {
+    $output = array(
+      '#theme' => $pager_theme,
+      '#tags' => $tags,
+      '#element' => $this->options['id'],
+      '#parameters' => $input,
+      '#quantity' => $this->options['quantity'],
+    );
     return $output;

+++ b/core/modules/views/lib/Drupal/views/Plugin/views/pager/Mini.phpundefined
@@ -101,11 +101,12 @@ function render($input) {
+    $output = array(
+      '#theme' => $this->themeFunctions(),
+      '#parameters' => $input,
+      '#element' => $this->options['id'],
+      '#tags' => $tags,
+    );
     return $output;

we can just return the array... no need to set $output :)

mondrake’s picture

Assigned: Unassigned » mondrake

Shouldn't be too hard to put tests in. I'll try to take it on.

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Closed (won't fix)
FileSize
8.19 KB
8.39 KB

#5

- return array directly - OK
- I also changed the docs of the PagerPluginBase render method - in fact this method is no longer 'rendering' the pager, but just returning a render array
- as to the test, I had a go but can get them to work - the idea being to refresh via AJAX call the view preview and then check if all expected elements are there. But the test doesn't work either with or without the patched plugins.

.. so this patch will fail, please anyone can take it up?

mondrake’s picture

Status: Closed (won't fix) » Needs review

urgh

Status: Needs review » Needs work

The last submitted patch, 2030293-7.patch, failed testing.

mondrake’s picture

Assigned: Unassigned » mondrake

Maybe I found a way. Patch soon.

mondrake’s picture

Status: Needs work » Needs review
FileSize
3.11 KB
8.36 KB
5.26 KB

Okay, so I found out how to render the entire view preview invoking the renderPreview method of ViewUI.

The test-only patch will show the failure (the Twig_Error_Runtime in summary both for Full and Mini plugins), and the full patch should go green.

(if bot agrees of course)

mondrake’s picture

Assigned: mondrake » Unassigned

unassigning

mondrake’s picture

Issue tags: -Needs tests

#11: 2030293-11-test_only.patch queued for re-testing.

mondrake’s picture

Issue tags: +Needs tests

#11: 2030293-11-test_only.patch queued for re-testing.

R.Hendel’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
129.19 KB

For testing I created a new view at simplytest.me containing a pager .
It displays pager in preview so I think this patch works very well.

Here you can see views preview with pager:

2030293-16.png

YesCT’s picture

Issue tags: -Needs tests

has tests, and the code looks good.

no api changes, I think.

Dries’s picture

Status: Reviewed & tested by the community » Needs review

For some reason, the test-only patch in #11 didn't actually fail. This suggests that the test may need work. I'm moving this to 'needs work' so we can investigate what is wrong and fix it. We should be close though ... :)

mondrake’s picture

Status: Needs review » Needs work

Yes, the test-only patch cannot reproduce the Twig runtime exception. Needs work.

mondrake’s picture

Status: Needs work » Needs review
Issue tags: +Twig, +Twig engine
FileSize
6.49 KB

I'm not Twig expert, but it looks like Twig is 'compiling' on first call the templates it renders. If on compile the {pager} element in the template is a render array, then it is no longer possible to pass pre-rendered html in further calls. Vice versa, if on compile the {pager} is a string, then following calls can use either render array or string. That's why test-only in #11 doesn't fail (both plugins use theme() instead of render array).

The patch attached is just a half-way between test-only and full patch in #11 - the theme() call is changed to render array for Full pager, but not the Mini one. So on 'compile' the Full is 'fixing' that {pager} should be a render array, and the Mini pager (still theme()) fails (well should :)).

Maybe review from Twig experts could help, tagging.

Status: Needs review » Needs work

The last submitted patch, 2030293-19-test_only.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review

Setting to 'needs review' for review of test in #19.

R.Hendel’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
76.4 KB

I cannot judge how serious it is, that automatic test results in one exception.
But like before I tested patch I in the same way I described in #15and found no errors.
So in my opinion the patch works fine.

Here you find screenshot:

2030293-22.png

dawehner’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +VDC

This is looking great and is nearly RTBC.

Ensure to tag everything as VDC, if it is related with views.

+++ b/core/modules/views/lib/Drupal/views/Plugin/views/pager/Full.phpundefined
@@ -78,10 +78,9 @@ public function summaryTitle() {
   function render($input) {

+++ b/core/modules/views/lib/Drupal/views/Plugin/views/pager/Mini.phpundefined
@@ -93,7 +93,7 @@ public function postExecute(&$result) {
   function render($input) {

Here is a small follow up which is unrelated: #2036087: Add public identifier to all render methods in all views plugins in core

+++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/PreviewTest.phpundefined
@@ -7,6 +7,9 @@
+use SimpleXMLElement;

@@ -80,6 +83,101 @@ function testPreviewUI() {
+    $result = simplexml_load_string('<viewPreview>' . $rendered_preview . '</viewPreview>');

@@ -93,4 +191,21 @@ public function testPreviewController() {
+   * @param SimpleXMLElement $element
+   *   The element to test.
...
+  protected function assertClass(SimpleXMLElement $element, $class, $message = NULL) {

Afaik we add the root namespace directly here, so

+++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/PreviewTest.phpundefined
@@ -80,6 +83,101 @@ function testPreviewUI() {
+  function testPreviewWithPagersUI() {

let's add a public identifier

mondrake’s picture

Status: Needs work » Needs review
FileSize
1.89 KB
8.33 KB

Thanks for reviews!

@23, patch attached

- removed use SimpleXMLElement;
- added public identifier to function testPreviewWithPagersUI()
- corrected a couple of glitches

I'd leave the addition of public identifier to the render method to the follow-up.

dawehner’s picture

Nearly :)

+++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/PreviewTest.phpundefined
@@ -93,4 +190,21 @@ public function testPreviewController() {
+   * @param SimpleXMLElement $element

This should use "\" as well.

mondrake’s picture

Status: Needs review » Needs work
YesCT’s picture

Status: Needs work » Needs review
FileSize
601 bytes
8.33 KB

just #25.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Thanks!

mondrake’s picture

Assigned: Unassigned » mondrake
Status: Reviewed & tested by the community » Needs work

I am going to post a new patch with better tests soon.

mondrake’s picture

Test changed to use drupalGet and drupalPostAJAX to mimick actual sequence of client-server steps.

So we can stay more high level here, and avoid instantiating a ViewsUI class to call the renderPreview method.

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
dawehner’s picture

+++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/PreviewTest.phpundefined
@@ -80,6 +80,113 @@ function testPreviewUI() {
+  public function testPreviewWithPagersUI() {

I am wondering whether we could reuse the test code between mini and full pagers a bit.

mondrake’s picture

Assigned: Unassigned » mondrake
Status: Needs review » Needs work

We certainly could put drupalGet, drupalPostAJAX and the checks up to the number of items on the page in a separate protected function. Then we may drop testPreviewController() as that would be redundant. Patch tomorrow.

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
FileSize
3.83 KB
8.65 KB

#32 and #33

Still, #19 is the only way I could find to have a test failure demonstrating the failure reported in the issue summary. We need have Twig 'compiling' the template based on a render array before it fails on pre-rendered HTML. That means at least one of the two plugins need to be changed to render array for the other one to fail. So no clear cut test-only patch.

R.Hendel’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
81.76 KB

I tested patch #34 at simplytest.me and found pager displayed at UI.

So just from the behaviour of views this patch works fine for me:

2030293-35.png

dawehner’s picture

+1 for the code

I am wondering whether there is an explicit test somewhere which checks that stuff is just rendered once. This might be hard

+++ b/core/modules/views/lib/Drupal/views/Plugin/views/pager/Full.phpundefined
@@ -90,13 +89,13 @@ function render($input) {
+      '#theme' => $this->view->buildThemeFunctions('pager'),

As a follow up I would suggest to use $this->themeFunctions which would require a "theme = 'pager' and register_theme = FALSE" in the annotation.

mondrake’s picture

Issue tags: -Twig, -VDC, -Twig engine

#34: 2030293-view_preview-34.patch queued for re-testing.

Status: Reviewed & tested by the community » Needs work
Issue tags: +Twig, +VDC, +Twig engine

The last submitted patch, 2030293-view_preview-34.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review
FileSize
858 bytes
8.68 KB
mondrake’s picture

Status: Needs review » Reviewed & tested by the community

Was RTBC before re-roll.

mondrake’s picture

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/PreviewTest.phpundefined
@@ -80,17 +80,128 @@ function testPreviewUI() {
+    // Verify elements and links to pages.
+    foreach ($elements as $page => $element) {
+      // Make element/page index 1-based.
+      $page++;
+      // We expect to find 5 elements: current page == 1, links to pages 2 and
+      // and 3, links to 'next >' and 'last >>' pages.
+      switch ($page) {
+        case 1:
+          $this->assertClass($element, 'pager-current', 'Element for current page has .pager-current class.');
+          $this->assertFalse(isset($element->a), 'Element for current page has no link.');
+          break;
+
+        case 2:
+        case 3:
+          $this->assertClass($element, 'pager-item', "Element for page $page has .pager-item class.");
+          $this->assertTrue($element->a, "Link to page $page found.");
+          break;
+
+        case 4:
+          $this->assertClass($element, 'pager-next', "Element for next page has .pager-next class.");
+          $this->assertTrue($element->a, "Link to next page found.");
+          break;
+
+        case 5:
+          $this->assertClass($element, 'pager-last', "Element for last page has .pager-last class.");
+          $this->assertTrue($element->a, "Link to last page found.");
+          break;
+
+      }
+    }

I think the whole foreach loop here is adding complexity... it is simpler and more explicit to write it like this... (also 11 lines of executable code vs 22 in the original... less to maintain :) )

    // We expect to find 5 elements: current page, links to pages 2 and
    // and 3, links to 'next >' and 'last >>' pages.
    $this->assertEqual(count($elements), 5, 'Full pager has five list items.');
    // Current page.
    $this->assertClass($elements[0], 'pager-current', 'Element for current page has .pager-current class.');
    $this->assertFalse(isset($elements[0]->a), 'Element for current page has no link.');
    // Link to page 2.
    $this->assertClass($elements[1], 'pager-item', "Element for page 2 has .pager-item class.");
    $this->assertTrue($elements[1]->a, "Link to page 2 found.");
    // Link to page 3.
    $this->assertClass($elements[2], 'pager-item', "Element for page 3 has .pager-item class.");
    $this->assertTrue($elements[2]->a, "Link to page 3 found.");
    // Link to next.
    $this->assertClass($elements[3], 'pager-next', "Element for next page has .pager-next class.");
    $this->assertTrue($elements[3]->a, "Link to next page found.");
    // Link to last.
    $this->assertClass($elements[4], 'pager-last', "Element for last page has .pager-last class.");
    $this->assertTrue($elements[4]->a, "Link to last page found.");
+++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/PreviewTest.phpundefined
@@ -80,17 +80,128 @@ function testPreviewUI() {
+    // Verify elements and links to pages.
+    foreach ($elements as $page => $element) {
+      // Make element/page index 1-based.
+      $page++;
+      // We expect to find 3 elements: previous page (with no link), current page == 1,
+      // and link to 'next >' page.
+      switch ($page) {
+        case 1:
+          $this->assertClass($element, 'pager-previous', 'Element for previous page has .pager-previous class.');
+          $this->assertFalse(isset($element->a), 'Element for previous page has no link.');
+          break;
+
+        case 2:
+          $this->assertClass($element, 'pager-current', 'Element for current page has .pager-current class.');
+          $this->assertFalse(isset($element->a), 'Element for current page has no link.');
+          break;
+
+        case 3:
+          $this->assertClass($element, 'pager-next', "Element for next page has .pager-next class.");
+          $this->assertTrue($element->a, "Link to next page found.");
+          break;
+
+      }
+    }

Add this too...

    // We expect to find 3 elements: previous page (with no link), current page == 1,
    // and link to 'next >' page.
    $this->assertEqual(count($elements), 3, 'Mini pager has three list items.');
    // Previous with no link because we are viewing the first page.
    $this->assertClass($elements[1], 'pager-previous', 'Element for previous page has .pager-previous class.');
    $this->assertFalse(isset($elements[1]->a), 'Element for previous page has no link.');
    // Current page.
    $this->assertClass($elements[1], 'pager-current', 'Element for current page has .pager-current class.');
    $this->assertFalse(isset($elements[1]->a), 'Element for current page has no link.');
    // Link to next.
    $this->assertClass($elements[2], 'pager-next', "Element for next page has .pager-next class.");
    $this->assertTrue($elements[2]->a, "Link to next page found.");
mondrake’s picture

Assigned: Unassigned » mondrake

Makes sense...

mondrake’s picture

Assigned: mondrake » Unassigned
Status: Needs work » Needs review
FileSize
4.5 KB
8.25 KB

Per #42

Status: Needs review » Needs work
Issue tags: -Twig, -VDC, -Twig engine

The last submitted patch, 2030293-view_preview-43.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review
Issue tags: +Twig, +VDC, +Twig engine

#44: 2030293-view_preview-43.patch queued for re-testing.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Really good idea!

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 982452f and pushed to 8.x. Thanks!

Automatically closed -- issue fixed for 2 weeks with no activity.