Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

The Power of require

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.

Yo friends! I am ecstatic about this tutorial on Webpack... because we've packed a ton of great stuff into it. But it's more than that! Webpack is going to change the way you develop front-end code, big time. And that's the real reason I'm so pumped!

But... Webpack is tough! And sometimes, it's weird and it mis-behaves and it's frustrating. Huh, it's kind of a like baby - super great and all... but with a mind of its own.

Anyways, that's why we're here: to unpack this beast from beginner to pro. In just a little while, you're going to have new super-powers that you never dreamed of.

Oh, what does Webpack actually do? Did I not mention that? I'll show you soon.

Grab the Code!

Like always, JavaScript is best written together, so download the course code from this page to code along with me. When you unzip the file, you'll find a start/ directory inside that has the same code you see here. Oh, and if you've been coding along with the first two JavaScript tutorials - you're amazing! But also, make sure to download the new code: I've made a few tweaks since the last tutorial to make things more interesting.

Then, open the README.md file for fascinating instructions on how to get the project setup. The last step will be to find your favorite terminal, move into the app, and run:

php bin/console server:run

to start the built-in PHP web server.

Pull up the site in your browser: http://localhost:8000. Welcome to Lift Stuff! Log in as ron_furgandy, password pumpup. Over the past 2 courses, we've lovingly built this activity-tracker-for-programmers into a nice, JavaScript-powered front-end. Now, we're going to revolutionize the way that code is organized.

Node and require

What am I talking about? Well, in the last tutorial, in addition to running JavaScript in the browser, we actually wrote a bit of Node.js - aka JavaScript that runs right on your server. Open up play.js:

7 lines play.js
let foods = new Set();
foods.add('gelato');
foods.add('tortas');
foods.add('gelato');
console.log(foods);

We used this as a simple way to test out new ES6 features.

To execute this, open a new terminal tab and run:

node play.js

Awesome! If you look at most Node.js code, one thing will jump out immediately: the require() function. On the surface, it's a lot like PHP's require statement: it allows you to separate code into multiple files.

Try it out: at the root of our project, create a new foods.js file. Then, copy the old foods code from play.js, delete it, and paste it in foods.js:

7 lines foods.js
let foods = new Set();
foods.add('gelato');
foods.add('tortas');
foods.add('gelato');
... lines 5 - 7

Modules & module.exports

Now, as you're probably expecting, we're going to require foods.js from play.js. But there is one really important difference between the way require works in PHP versus Node. In PHP, when you require a file, you magically have access to all functions, classes or variables inside. But in Node, that's not true: you need to explicitly export a value from the required file.

How? By saying module.exports = foods:

7 lines foods.js
let foods = new Set();
foods.add('gelato');
foods.add('tortas');
foods.add('gelato');
module.exports = foods;

By the way, in JavaScript, each file is known as a module and is executed in isolation. Nothing is returned from this file other than what we export.

Ok! Back in play.js, we can now say, const foods = require('./foods'):

4 lines play.js
const foods = require('./foods');
console.log(foods);

Before I say more, try it! Head back to your terminal and re-run the code:

node play.js

It still works! Back in the code, notice that we do not need the .js on the end of the filename:

4 lines play.js
const foods = require('./foods');
... lines 2 - 4

We can add it - that would totally work. But if it's not there, Node knows to look for foods.js.

Also, that ./ before foods is no accident: that's super important. When a path starts with a ., Node knows to look for that file relative to this file. If we just said foods without the ./, well, that means something very different. More on that later.

Using require in the Browser

So the require() function is an awesome feature. The question is: can we use this in the browser JavaScript world? Because if we could, just imagine how easy it would be to organize our JavaScript!

And yes! We can totally get it working... otherwise this would be a really short tutorial.

But first, a little bit of cleanup: open app/Resources/views/lift/index.html.twig:

... lines 1 - 53
{% block javascripts %}
{{ parent() }}
<script src="https://cdn.jsdelivr.net/sweetalert2/6.1.0/sweetalert2.min.js"></script>
<script src="{{ asset('assets/dist/RepLogApp.js') }}"></script>
<script>
$(document).ready(function() {
var $wrapper = $('.js-rep-log-table');
var repLogApp = new RepLogApp($wrapper);
});
</script>
{% endblock %}

This is the template for the main page. And at the end of the last tutorial, we used a library called Babel to "transpile" our source RepLogApp.js into a dist/RepLogApp.js file. We are going to use Babel again, but for now, delete the dist file. Then, point our page back to the source RepLogApp.js:

... lines 1 - 53
{% block javascripts %}
... lines 55 - 57
<script src="{{ asset('assets/js/RepLogApp.js') }}"></script>
... lines 59 - 65
{% endblock %}

This file contains two classes: RepLogApp and, near the bottom, another called Helper:

'use strict';
(function(window, $, Routing, swal) {
... lines 4 - 6
class RepLogApp {
... lines 8 - 194
}
/**
* A "private" object
*/
class Helper {
... lines 201 - 228
}
... lines 230 - 247
})(window, jQuery, Routing, swal);

Hey! For organization, let's move this class into its own file. In that same directory, create a new RepLogAppHelper.js file. I'll add the 'use strict'; on top and then paste the class:

'use strict';
/**
* A "private" object
*/
class Helper {
constructor(repLogs) {
this.repLogs = repLogs;
}
calculateTotalWeight() {
return Helper._calculateWeights(
this.repLogs
);
}
getTotalWeightString(maxWeight = 500) {
let weight = this.calculateTotalWeight();
if (weight > maxWeight) {
weight = maxWeight + '+';
}
return weight + ' lbs';
}
static _calculateWeights(repLogs) {
let totalWeight = 0;
for (let repLog of repLogs) {
totalWeight += repLog.totalWeightLifted;
}
return totalWeight;
}
}
... lines 36 - 38

At the bottom, add module.exports = Helper:

'use strict';
/**
* A "private" object
*/
class Helper {
... lines 7 - 34
}
module.exports = Helper;

You can export anything you want from a module - a value, like we did earlier - a class, a function, an object - whatever!

Back in RepLogApp.js, now that the Helper class is gone... well, we're not going to have a good time. On line 10, PhpStorm is giving me a cryptic error: element is not exported... a funny way of saying "Variable undefined"!

Fix this: at the top, add const Helper = require('./RepLogAppHelper'):

'use strict';
const Helper = require('./RepLogAppHelper');
(function(window, $, Routing, swal) {
... lines 6 - 215
})(window, jQuery, Routing, swal);

My editor is happy!

Based on what we saw in Node... this should just work! Back in my browser, I'll open the console and then, refresh. Yep, we somehow knew life wouldn't be so simple. We get an error:

require is not defined

Here's the deal: the require() function does not work in any browser. And it's not that browsers are behind... it's just not possible to make it work!

Think about it: in PHP, when we use the require statement, we're reading a file from our file system... which is basically instant. But on the web, a browser would need to go download that file. Imagine if we waited while it downloaded this file... then this file required 5 other files.... so we waited for those... then those files required 10 other files... so we finally decide to go have lunch while the web page loads. It just doesn't work!

Enter Webpack.

Leave a comment!

19
Login or Register to join the conversation
Max A. Avatar
Max A. Avatar Max A. | posted 4 years ago | edited

there What icon plugin do you use for PhpStorm?

I would like to install that :)

Reply

Yo Max A.!

Um... dang! I have no idea! I actually don't think I have anything special as far as icons go. What icons are you seeing that are different on my setup? I'm happy to share anything I can :).

Cheers!

Reply
Max A. Avatar
Max A. Avatar Max A. | weaverryan | posted 4 years ago | edited

weaverryan
Like this one

https://drive.google.com/op...

Reply

Hey Max A.!

Interesting. I can answer a few of those. For the Composer files, I use the "PHP composer.json support" plugin - which I think adds this. For the .gitignore file, I use the ".gitignore" plugin.

But for the others - I'm not sure - I don't believe I have anything special installed to have the JSON icon, for example. That is a mystery :).

Cheers!

Reply
Shaun T. Avatar
Shaun T. Avatar Shaun T. | posted 4 years ago

Hey guys,

In the original replog app you were using underscore for templating, but it seems like you are not in this version, any reason for this?

Reply

Hey Shaun T.!

Good catch ;). No big reason in particular. But, in the 2nd screencast in this track, we introduce template strings, which allow you to create a simple templating engine without any outside tools - https://symfonycasts.com/sc.... Of course, it's not that there is a disadvantage to using _ for templating - in fact, it's got more features. But for our simple use-case, we could easily use template strings to keep everything in plain, boring JavaScript.

Cheers!

Reply
Shaun T. Avatar
Shaun T. Avatar Shaun T. | weaverryan | posted 4 years ago | edited

Awesome, I'll check that out, thanks weaverryan :)

Reply
Default user avatar
Default user avatar Michael | posted 5 years ago

Hi Ryan, I think you have typo in login, you have ron_furgundy but it should be ron_furgandy

Reply

Hey Michael

You are totally right! We have a typo in our script.
Thanks for letting us know, we will fix it as soon as possible.

Cheers!

Reply
Maki V. Avatar
Maki V. Avatar Maki V. | posted 5 years ago

Hi guys!

As always - thanks for interesting stuff! I am a Symfony backend developer, but very interested in code design and philosophy. I have been wondering for quite some time about webpacking - as for doing so, your application becomes depending on enabled javascript in your browser. My question is more philosophical, but in my opinion very important.

Should application's core functionality be depending on enabled javascript inside your browser?

This question came up to my mind after reading this interesting article on server medium.org: https://medium.freecodecamp...

and seeing this diagram with an interesting concept: https://cdn-images-1.medium...

I like the idea that backend just sends data via some kind of API and javascript using React renders the view - but in doing so you are depending on javascript in your browser and in doing so - breaking application for your non-js users.

What are your thoughts about this concept, guys?

Reply

Hey Milan V.!

GREAT question. And, it's something I've thought a lot about, but am not exactly an expert. But, for what it's worth, here are my thoughts.

Years ago, I would develop a site that worked with with and without JavaScript. I don't do that anymore. First, I think we're beyond the time where users having "JavaScript disabled" is a problem. Also (and I'm FAR from an expert on this topic), I no longer think that a well-written JavaScript application has any "accessibility" concerns. Years ago, you may have wanted to make your site work without JavaScript, because JavaScript confused screen readers for blind users. That is no longer the case (you CAN still build badly accessible sites using JS, but the point is, accessibility tools now understand JS, so if you render an accessible-friendly site purely in JS, it's no problem).

So, if JS doesn't present accessibility problems, nor (probably) search engine indexing problems, and 0% of your users have JavaScript disabled, it becomes very difficult to make a business case for the added time and complexity if making your site work without JS. If you turn JS off for KnpU, the site will somewhat work, but the most important parts will be broken.

As an experiment, I turned JavaScript off and went to a few HUGE news sites. The first site I went to did not load at all, beyond the header and the footer. Yep, 0 content with JavaScript disabled. If a huge new site doesn't care, you probably shouldn't either.

So, yea! When we think about rendering a pure JS front-end versus something more traditional, I give more weight to (A) what is your team's expertise with JavaScript (B) Do you really need a full JS frontend, or would it be easy enough to render HTML and make parts of your site super fancy and (C), even though it shouldn't be a problem, I still somewhat fear search engine indexing problems. But, I think this may be an out-of-date view, honestly.

Anyways, I hope this helps!

Cheers!

3 Reply
Maki V. Avatar

Yo @weaverryan!

Love your thoughts! I think you just covered all of the main issues people could have. :))

I would like to add just a few bits out of my head that indirectly confirms your thoughts.

If you (the person reading this) are a Google Chrome user, press CTRL + SHIFT + I (open your DevTools) and navigate into Settings, you'll notice that the "Disable JavaScripts" option is located under the "Debugger" option, indicating, that it is not meant for casual user to operate like that. I think the main purpose of that option still available in the browsers is just for a testing environment that might come out by using certain browser add-ons. Thus, Google developers don't actually think that disabled JS is a standard behaviour (and I certainly cannot argue with Google on that one :)).

There are yearly a lot of statistics about the most used programming languages all around the world. JavaScript is still on the front lines as "One of the most used programming language of all time". If people think that JS has no value, they would abandon it and use alternatives that might fulfil their needs.

and lastly - I am so glad you made your own research about how many sites work without enabled JS! This is one of the fastest growing real estate sites in the Czech Republic: https://www.ulovdomov.cz/ (for anyone interested in Czech - "ulov domov" means "catch home"). I am using it as an example because it is composed more-less only by JS and remote API. Search engines are aware of pages located on that domain and work nicely with its routing system. If you disable JS in your browser and try to access the site, 0% of it will work. Another indication that developers just count with enabled JS nowadays.

I would just add, that it is nice to let a user know about need for enabled JS by some kind of notification - to let them be aware of that fact.

Anyway, thanks for your help, Ryan! Always love your comments/videos and hope we will discuss more stuff in the future! :)

3 Reply

Hey Milan V.
Thanks for sharing your thoughts :)
Showing a message when a user has JS disabled, sounds like a good idea

Cheers!

Reply
Default user avatar
Default user avatar gaotian | posted 5 years ago

Hi,
Thanks for the tuto, I think there may be a small typo in the script with : "const Helper = require('./RepLogHelper'); " for "const Helper = require('./RepLogAppHelper');" (not in the video and in the text before ^^)

Reply

Hey Gaotian!

You are right, we have a tiny error in our script, we are going to fix it as soon as possible :)
Thanks for letting us know!

Have a nice day

Reply
Default user avatar

nowadays it works better with: import Helper from './RepLogAppHelper'
(otherwise I got the error: Uncaught TypeError: Class constructor Helper cannot be invoked without 'new')

love your tutos <3

Reply
Default user avatar
Default user avatar mudita again | mudita | posted 5 years ago

oups wrong, this is fine, I must have done something wrong, please delete my com (^^;)
(also I checked the option disable HTTP cache when dev tools are open)

Reply

Hey Mudita,

Don't worry about your comments, we're not going to remove them ;)
Glad you solved the problem!

Cheers!

Reply
Cat in space

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

This tutorial explains the concepts of an old version of Webpack using an old version of Symfony. The most important concepts are still the same, but you should expect significant differences in new versions.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.3.*", // v3.3.16
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.0
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.3
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "^2.0", // v2.1.2
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0" // v3.3.5
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "dependencies": [],
    "devDependencies": {
        "babel-core": "^6.25.0", // 6.25.0
        "babel-loader": "^7.1.1", // 7.1.1
        "babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
        "babel-preset-env": "^1.6.0", // 1.6.0
        "bootstrap-sass": "^3.3.7", // 3.3.7
        "clean-webpack-plugin": "^0.1.16", // 0.1.16
        "copy-webpack-plugin": "^4.0.1", // 4.0.1
        "core-js": "^2.4.1", // 2.4.1
        "css-loader": "^0.28.4", // 0.28.4
        "extract-text-webpack-plugin": "^3.0.0", // 3.0.0
        "file-loader": "^0.11.2", // 0.11.2
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.2.1", // 3.2.1
        "lodash": "^4.17.4", // 4.17.4
        "node-sass": "^4.5.3", // 4.5.3
        "resolve-url-loader": "^2.1.0", // 2.1.0
        "sass-loader": "^6.0.6", // 6.0.6
        "style-loader": "^0.18.2", // 0.18.2
        "sweetalert2": "^6.6.6", // 6.6.6
        "webpack": "^3.4.1", // 3.4.1
        "webpack-chunk-hash": "^0.4.0", // 0.4.0
        "webpack-dev-server": "^2.6.1", // 2.6.1
        "webpack-manifest-plugin": "^1.2.1" // 1.2.1
    }
}
userVoice