Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Ajax Pagination & CSS/JS

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Now that we're rendering these recipe items via the grid block type, check out what we can do. Click the grid, go to the design tab and then check "Enable pagination". Then you can choose between a pager style with page links, like 1, 2, 3 and 4, or just a "load more" button. Let's use that one.

All right, hit "Publish and continue editing". Then... once that saves, refresh to see... absolutely nothing! The pagination is powered entirely via JavaScript and Ajax. And we don't see anything because we haven't, yet, included the JavaScript needed onto our page.

Including the CSS/JS Templates

Adding it is pretty easy. Go to templates/base.html.twig. Up here in the head area, we're going to include two templates. The first is: @NetgenLayoutsStandard/page_head.html.twig... and pass this an extra variable: full: true:

<!DOCTYPE html>
<html>
<head>
... lines 4 - 7
{{ include('@NetgenLayoutsStandard/page_head.html.twig', { full: true }) }}
... lines 9 - 16
</head>
... lines 18 - 69
</html>

This will load the CSS and JavaScript that support these gallery items down here. I'm not gonna talk about these gallery blocks in this tutorial, but they're basically like a list or grid block, except that they have JavaScript to turn them into sliders or thumb galleries.

So this includes the CSS and JavaScript for those, as well as a small grid CSS file to help render the grid columns on your page in case you don't have Bootstrap. The full: true tells it to bring in jQuery as well as two other JavaScript libraries called magnific-popup and swiper. All of these are needed for those gallery blocks.

So, yes, if you're not using one of those gallery blocks, you could avoid including this file entirely. But I'll leave it.

But notice, I didn't say anything about pagination. For that, we need to include a second template. Copy this line, paste, remove the word Standard and this doesn't need the full variable:

<!DOCTYPE html>
<html>
<head>
... lines 4 - 7
{{ include('@NetgenLayoutsStandard/page_head.html.twig', { full: true }) }}
{{ include('@NetgenLayouts/page_head.html.twig') }}
... lines 10 - 16
</head>
... lines 18 - 69
</html>

This template is dead simple: it brings in a tiny bit of CSS and a little bit of JavaScript to power Ajax pagination. And these are the only two templates that you'll ever need to include for Layouts JavaScript and CSS.

Adding the "ajax" Item Template

Refresh and... there it is! And when we click the new link... it explodes with a 500 error! Whoops.

Open that URL in a new tab. Interesting:

No template match could be found for "item_view" view and content "ajax".

When we click "Load more", no surprise, that Ajax call renders the next recipe items. You might think that this would re-use our "frontend" item view template. But... there's actually a different section specifically for when content is rendered via Ajax. Copy the default frontend section entirely, paste, then change it to ajax:

netgen_layouts:
... lines 2 - 12
view:
item_view:
... lines 15 - 29
ajax:
# this key is not important
latest_recipes_default:
template: 'nglayouts/frontend/recipe_item.html.twig'
match:
item\value_type: 'doctrine_recipe'

Nothing else needs to change: when we're in ajax mode, use the normal frontend template.

Now, if we refresh the Ajax endpoint... it works! Reload the homepage and click "Load more". That is so nice!

Translating the Pagination Button

Though, minor thing, our designers really want to use the text "Show More". No problem: everything that Layouts renders is processed through the translator. Click the translation icon on the web debug toolbar. Oh, there it is! Apparently the translation key is collection.pager.load_more.

Copy that... then go open our translation file - nglayouts.en.yaml - and paste. My editor changed the format... which actually would work... but I'll go back to the flatter format. Set this to "Show More":

... line 1
collection.pager.load_more: 'Show More'

Spin over and... we got it!

CSS Changes to Pagination

Ok, one more change to make our designers happy. Inspect element on the button. Layouts adds a bunch of classes, which are styled via that CSS we included. And, of course, we can override that if needed.

In our editor, open assets/styles/app.css. As a reminder, we're already running Webpack Encore in the background. So, if we change this file, that change will automatically be rebuilt and used on the frontend.

At the bottom, I'll paste some CSS to give that button more margin but no border:

... lines 1 - 101
.ajax-navigation {
margin-top: 2rem;
}
.ajax-load-more {
border: none;
}

Flip back over, refresh and... our designers are happy.

So thanks to layouts, we get free Ajax pagination, which we can pretty easily customize. That's sweet.

Grids vs Custom Twig Content

At this point, because we're able to render grids and lists of recipes, we could go into the "Recipes List" layout and replace this hardcoded HTML, which comes from the template: templates/recipes/list.html.twig. Yup, we could, in theory, remove this and replace it with a list block.

The only problem... is that it wouldn't look quite right. Instead of rendering like it does now, Layouts would use our item template: so each item would look like it does on the homepage.

Now, we can fix that by creating a second way to render recipe items, and we will talk about that later. But I'm bringing this up for an important reason. Unless we're planning to reuse this list and how it looks on other pages on our site, there's no huge benefit to doing the work to convert it into something that we can render via Layouts. Since it's only used here, rendering it via Twig is perfectly fine.

Next: let's improve the recipe system by making it possible to manually select items.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.7", // v3.7.0
        "doctrine/doctrine-bundle": "^2.7", // 2.7.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.13", // 2.13.3
        "easycorp/easyadmin-bundle": "^4.4", // v4.4.1
        "netgen/layouts-contentful": "^1.3", // 1.3.2
        "netgen/layouts-standard": "^1.3", // 1.3.1
        "pagerfanta/doctrine-orm-adapter": "^3.6",
        "sensio/framework-extra-bundle": "^6.2", // v6.2.8
        "stof/doctrine-extensions-bundle": "^1.7", // v1.7.0
        "symfony/console": "5.4.*", // v5.4.14
        "symfony/dotenv": "5.4.*", // v5.4.5
        "symfony/flex": "^1.17|^2", // v2.2.3
        "symfony/framework-bundle": "5.4.*", // v5.4.14
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/proxy-manager-bridge": "5.4.*", // v5.4.6
        "symfony/runtime": "5.4.*", // v5.4.11
        "symfony/security-bundle": "5.4.*", // v5.4.11
        "symfony/twig-bundle": "5.4.*", // v5.4.8
        "symfony/ux-live-component": "^2.x-dev", // 2.x-dev
        "symfony/ux-twig-component": "^2.x-dev", // 2.x-dev
        "symfony/validator": "5.4.*", // v5.4.14
        "symfony/webpack-encore-bundle": "^1.15", // v1.16.0
        "symfony/yaml": "5.4.*", // v5.4.14
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.3
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "symfony/debug-bundle": "5.4.*", // v5.4.11
        "symfony/maker-bundle": "^1.47", // v1.47.0
        "symfony/stopwatch": "5.4.*", // v5.4.13
        "symfony/web-profiler-bundle": "5.4.*", // v5.4.14
        "zenstruck/foundry": "^1.22" // v1.22.1
    }
}
userVoice