Making your Custom Controllers Lazy

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Stimulus bridge has one more trick. On the analyzer tab, the 3 files that are loaded on every page are now these 2 dark blue files and this 1 light blue file. So the biggest JavaScript modules now are sweetalert2, Stimulus itself and something called core-js.

core-js represents our Polyfills. For example, the web.url.js thing is what gives us the URLSearchParams object that we've used a few times.

So other than the Polyfills and Stimulus, the biggest item in here is definitely sweetalert2. And, again, that's kind of unfortunate... because that's only used on the cart page. Could we do the same "lazy" trick with our own controllers?

The stimulusFetch: lazy Comment

Totally! Open assets/controllers/submit-confirm_controller.js. This is the one file that imports SweetAlert. Above the controller class, add a special comment: stimulusFetch colon then lazy in quotes.

... lines 1 - 4
/* stimulusFetch: 'lazy' */
export default class extends Controller {
... lines 7 - 52
}

This is a special syntax and feature from stimulus-bridge. The text inside the comments will be parsed as JSON, which is why we have the colon and quotes. The result of this is that... yea! Our controller will be lazy - just like the chartjs controller!

Head back to our terminal. Let's re-run our commands again to do one last analyzer dump:

yarn run --silent dev --json > stats.json

When we run the analyzer:

yarn webpack-bundle-analyzer stats.json public/build

Yes! This time sweetalert is in its own file! The only reason we don't also see our actual controller in this same file is that Webpack often splits vendor code and custom code into separate files for caching reasons. If we look around a bit... hmm... ah! Here it is! This new, tiny file contains our submit-confirm controller code. This will also be loaded asynchronously as soon as it's needed.

Let's see this in action! Go refresh the homepage... I close out some old tabs... then view the page source. The 3 files being loaded now are runtime, this long vendors-stimulus-bridge and app.js.

If you look at the analyzer... the 3 files are this app.js, the tiny runtime.js and... this vendors file. These are now way smaller than when we started.

Click into a product, then go back to the Network tools filtered to JavaScript files. Add the item to the cart... then click to the cart page.

Woohoo! The SweetAlert code and our submit-confirm controller were both just downloaded asynchronously! And everything still works! This does mean that, in theory, someone could click "Remove" so fast that the JavaScript isn't loaded on the page. If that's a problem, you should either avoid using fetch lazy, or disable this button on page load and enable it in your Stimulus controller.

Okay, enough with that crazy laziness! Next, after chatting with some of you lovely people over the past few weeks, I thought it might be nice to do a full example of submitting a form via Ajax, complete with validation errors and reloading part of the page after success. Oh, and we'll do all of this inside a modal... to make it more interesting.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=7.4.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "1.11.99.1", // 1.11.99.1
        "doctrine/annotations": "^1.0", // 1.11.1
        "doctrine/doctrine-bundle": "^2.2", // 2.2.3
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
        "doctrine/orm": "^2.8", // 2.8.1
        "phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
        "sensio/framework-extra-bundle": "^5.6", // v5.6.1
        "symfony/asset": "5.2.*", // v5.2.3
        "symfony/console": "5.2.*", // v5.2.3
        "symfony/dotenv": "5.2.*", // v5.2.3
        "symfony/flex": "^1.3.1", // v1.12.1
        "symfony/form": "5.2.*", // v5.2.3
        "symfony/framework-bundle": "5.2.*", // v5.2.3
        "symfony/property-access": "5.2.*", // v5.2.3
        "symfony/property-info": "5.2.*", // v5.2.3
        "symfony/proxy-manager-bridge": "5.2.*", // v5.2.3
        "symfony/security-bundle": "5.2.*", // v5.2.3
        "symfony/serializer": "5.2.*", // v5.2.3
        "symfony/twig-bundle": "5.2.*", // v5.2.3
        "symfony/ux-chartjs": "^1.1", // v1.2.0
        "symfony/validator": "5.2.*", // v5.2.3
        "symfony/webpack-encore-bundle": "^1.9", // v1.11.1
        "symfony/yaml": "5.2.*", // v5.2.3
        "twig/extra-bundle": "^2.12|^3.0", // v3.2.1
        "twig/intl-extra": "^3.2", // v3.2.1
        "twig/twig": "^2.12|^3.0" // v3.2.1
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.0
        "symfony/debug-bundle": "^5.2", // v5.2.3
        "symfony/maker-bundle": "^1.27", // v1.30.0
        "symfony/monolog-bundle": "^3.0", // v3.6.0
        "symfony/stopwatch": "^5.2", // v5.2.3
        "symfony/var-dumper": "^5.2", // v5.2.3
        "symfony/web-profiler-bundle": "^5.2" // v5.2.3
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@babel/preset-react": "^7.0.0", // 7.12.13
        "@popperjs/core": "^2.9.1", // 2.9.1
        "@symfony/stimulus-bridge": "^2.0.0", // 2.1.0
        "@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/Resources/assets", // 1.1.0
        "@symfony/webpack-encore": "^1.0.0", // 1.0.4
        "bootstrap": "^5.0.0-beta2", // 5.0.0-beta2
        "core-js": "^3.0.0", // 3.8.3
        "jquery": "^3.6.0", // 3.6.0
        "react": "^17.0.1", // 17.0.1
        "react-dom": "^17.0.1", // 17.0.1
        "regenerator-runtime": "^0.13.2", // 0.13.7
        "stimulus": "^2.0.0", // 2.0.0
        "stimulus-autocomplete": "^2.0.1-phylor-6095f2a9", // 2.0.1-phylor-6095f2a9
        "stimulus-use": "^0.24.0-1", // 0.24.0-1
        "sweetalert2": "^10.13.0", // 10.14.0
        "webpack-bundle-analyzer": "^4.4.0", // 4.4.0
        "webpack-notifier": "^1.6.0" // 1.13.0
    }
}