Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Multiple Pages / Entries

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.

This is all really nice... but, so far... it kinda looks like Webpack only works for single-page apps! I mean, if this were the only page in our app, we could write all of our JavaScript in the one entry file, require what we need and... be done!

But even our small app has another page: /login. And this page has its own custom JavaScript.... which right now, is done in this boring, old, non-Webpack-ified login.js file:

'use strict';
(function(window, $) {
$(document).ready(function() {
$('.js-recommended-login').on('click', '.js-show-login', function(e) {
e.preventDefault();
$('.js-recommended-login-details').toggle();
});
$('.js-login-field-username').on('keydown', function(e) {
const $usernameInput = $(e.currentTarget);
// remove any existing warnings
$('.login-long-username-warning').remove();
if ($usernameInput.val().length >= 20) {
const $warning = $('<div class="login-long-username-warning">This is a really long username - are you sure that is right?</div>');
$usernameInput.before($warning);
}
});
});
})(window, jQuery);

So... how can we also process this file through Webpack? We could require it from rep_log.js, but that's wasteful! We really only need this code on the login page.

Multiple Entries

The answer is... so simple: add a second entry called login that will load the assets/js/login.js file:

... lines 1 - 2
Encore
... lines 4 - 9
.addEntry('rep_log', './public/assets/js/rep_log.js')
.addEntry('login', './public/assets/js/login.js')
... lines 12 - 13
;
... lines 15 - 18

I want you to think of each entry as a separate page on your site. Or, you can think of each entry as a separate JavaScript application that runs on your site. Like, we have our main "rep log" application and also our "login" application.

Because we just changed the webpack config, go back and restart Encore:

yarn run encore dev --watch

Webpack-Sponsored Cleanup

And now we can improve things! First, remove that self-executing function:

'use strict';
... lines 2 - 4
$(document).ready(function() {
$('.js-recommended-login').on('click', '.js-show-login', function(e) {
e.preventDefault();
$('.js-recommended-login-details').toggle();
});
$('.js-login-field-username').on('keydown', function(e) {
const $usernameInput = $(e.currentTarget);
// remove any existing warnings
$('.login-long-username-warning').remove();
if ($usernameInput.val().length >= 20) {
const $warning = $('<div class="login-long-username-warning">This is a really long username - are you sure that is right?</div>');
$usernameInput.before($warning);
}
});
});

Then, more importantly, require the dependencies we need: in this case jQuery with const $ = require('jquery'):

'use strict';
const $ = require('jquery');
$(document).ready(function() {
$('.js-recommended-login').on('click', '.js-show-login', function(e) {
e.preventDefault();
$('.js-recommended-login-details').toggle();
});
$('.js-login-field-username').on('keydown', function(e) {
const $usernameInput = $(e.currentTarget);
// remove any existing warnings
$('.login-long-username-warning').remove();
if ($usernameInput.val().length >= 20) {
const $warning = $('<div class="login-long-username-warning">This is a really long username - are you sure that is right?</div>');
$usernameInput.before($warning);
}
});
});

That's it! Go back and... refresh! Bah:

require is not defined

Boo! My bad - I forgot to use the new built file. Open templates/bundles/FOSUserBundle/Security/login.html.twig. Point the script tag to build/login.js:

... lines 1 - 10
{% block javascripts %}
... lines 12 - 13
<script src="{{ asset('build/login.js') }}"></script>
{% endblock %}
... lines 16 - 72

And now... it works! When I type a really long username, this message appears thanks to that JavaScript.

The "layout" Entry

But... there's one last problem. Open the base layout file: base.html.twig. Yep, we also include one JavaScript file on every page:

... lines 1 - 98
{% block javascripts %}
... lines 100 - 104
<script src="{{ asset('assets/js/layout.js') }}"></script>
{% endblock %}
... lines 107 - 110

It doesn't do much... just adds a tooltip when you hover over your username:

'use strict';
(function(window, $) {
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
});
})(window, jQuery);

So... how do we handle this? How can we Webpackify this file? I mean, the layout is not its own page... so... can it be its own entry? The answer is... yes! Add another entry called layout and point it to assets/js/layout.js:

... lines 1 - 2
Encore
... lines 4 - 11
.addEntry('layout', './public/assets/js/layout.js')
... lines 13 - 14
;
... lines 16 - 19

Here's the deal: usually you will include exactly one script tag for a built JavaScript file on each page - like rep_log.js or login.js. But, if you have some JavaScript that should be included on every page, you can think of that JavaScript as its own, mini JS application. In that case, you'll have two built files per page: your layout JavaScript and your page-specific JavaScript... if you have any for that page.

Go back and restart Webpack so it reads the new config.

yarn run encore dev --watch

But... let's not refactor this file yet: we'll do that next. In base.html.twig, use the new file: build/layout.js:

... lines 1 - 98
{% block javascripts %}
... lines 100 - 104
<script src="{{ asset('build/layout.js') }}"></script>
{% endblock %}
... lines 107 - 110

Boom! Try it! Refresh the page! Yes! It still works. Next, let's refactor layout.js to remove the self-executing function and require its dependencies. But this time... there's a surprise!

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": "^7.2.0",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^1.6", // 1.8.1
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
        "doctrine/doctrine-fixtures-bundle": "~3.0", // 3.0.2
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.1
        "doctrine/orm": "^2.5", // v2.7.2
        "friendsofsymfony/jsrouting-bundle": "^2.2", // 2.2.0
        "friendsofsymfony/user-bundle": "dev-master", // dev-master
        "sensio/framework-extra-bundle": "^5.1", // v5.1.5
        "symfony/asset": "^4.0", // v4.0.4
        "symfony/console": "^4.0", // v4.0.4
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/form": "^4.0", // v4.0.4
        "symfony/framework-bundle": "^4.0", // v4.0.4
        "symfony/lts": "^4@dev", // dev-master
        "symfony/monolog-bundle": "^3.1", // v3.1.2
        "symfony/polyfill-apcu": "^1.0", // v1.7.0
        "symfony/serializer-pack": "^1.0", // v1.0.1
        "symfony/swiftmailer-bundle": "^3.1", // v3.1.6
        "symfony/twig-bundle": "^4.0", // v4.0.4
        "symfony/validator": "^4.0", // v4.0.4
        "symfony/yaml": "^4.0", // v4.0.4
        "twig/twig": "2.10.*" // v2.10.0
    },
    "require-dev": {
        "symfony/debug-pack": "^1.0", // v1.0.4
        "symfony/dotenv": "^4.0", // v4.0.4
        "symfony/phpunit-bridge": "^4.0", // v4.0.4
        "symfony/web-server-bundle": "^4.0" // v4.0.4
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@symfony/webpack-encore": "^0.19.0", // 0.19.0
        "bootstrap": "3", // 3.3.7
        "copy-webpack-plugin": "^4.4.1", // 4.4.1
        "font-awesome": "4", // 4.7.0
        "jquery": "^3.3.1", // 3.3.1
        "node-sass": "^4.7.2", // 4.7.2
        "sass-loader": "^6.0.6", // 6.0.6
        "sweetalert2": "^7.11.0", // 7.11.0
        "webpack-notifier": "^1.5.1" // 1.5.1
    }
}
userVoice