Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Modules: require() & import()

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.

Let's get back to talking about the real power of Webpack: the ability to import or require JavaScript files. Pretend that building this string is actually a lot of work. Or maybe it's something we need to re-use from somewhere else in our code:

15 lines assets/js/app.js
... lines 1 - 13
console.log('Hello Webpack Encore! Edit me in assets/js/app.js!!!');

So, we want to isolate it into its own file. If this were PHP, we would create a new file to hold this logic. In JavaScript, we're going to do the same thing.

In assets/js/, create a new file called get_nice_message.js. Unlike PHP, in JavaScript, each file that you want to use somewhere else needs to export something, like a function, object, or even a string. Do that by saying module.exports = and then the thing you want to export. Let's create a function() with one argument exclamationCount:

module.exports = function(exclamationCount) {
... line 2
};

Inside, let's go steal our string... then return that string and, to increase our fanciness, add '!'.repeat(exclamationCount):

module.exports = function(exclamationCount) {
return 'Hello Webpack Encore! Edit me in assets/js/app.js'+'!'.repeat(exclamationCount);
};

Yes. Because strings are objects in JavaScript, this works - it's kinda cool. By the way, when a JavaScript file exports a value like this, it's known as a "module". That's not a big deal, but you'll hear this term a lot: JavaScript modules. OooOOOoo. It just refers to what we're doing here.

Now go back to app.js. At the top, well... it doesn't need to be on top, but usually we organize the imports there, add const getNiceMessage = require('./get_nice_message');:

17 lines assets/js/app.js
/*
* Welcome to your app's main JavaScript file!
*
* We recommend including the built version of this JavaScript file
* (and its CSS file) in your base layout (base.html.twig).
*/
// any CSS you require will output into a single css file (app.css in this case)
require('../css/app.css');
// Need jQuery? Install it with "yarn add jquery", then uncomment to require it.
// const $ = require('jquery');
const getNiceMessage = require('./get_nice_message');
... lines 15 - 17

Notice the .js extension is optional, you can add it or skip it - Webpack knows what you mean. And because, key strokes are expensive... and programmers are lazy, you usually don't see it.

Also, that ./ at the beginning is important. When you're pointing to a file relative to the current one, you need to start with ./ or ../. If you don't, Webpack will think you're trying to import a third-party package. We'll see that soon.

And now that we have our getNiceMessage() function, let's call it! Pass it 5 for just the right number of excited exclamation points:

17 lines assets/js/app.js
... lines 1 - 13
const getNiceMessage = require('./get_nice_message');
console.log(getNiceMessage(5));

And because we're running the watch command in the background, when we refresh, it just works!

import Versus require

But! When we originally looked at the Webpack docs, they weren't using require() and module.exports! Nope, they were using import and export. It turns out, there are two valid ways to export and import values from other files... and they're basically identical.

To use the other way, remove module.exports and say export default:

export default function(exclamationCount) {
... line 2
};

That does the same thing. The default is important. With this syntax, a module, so, a file, can export more than one thing. We're not going to talk about that here, but most of the time, you'll want to export just one thing, and this default keyword is how you do that.

Next, back in app.js, the require changes to import getNiceMessage from './get_nice_message':

17 lines assets/js/app.js
... lines 1 - 13
import getNiceMessage from './get_nice_message';
... lines 15 - 17

That's it! That is 100% the same as what we had before. So, which should you use? Use this syntax. The require() function comes from Node. But the import and export syntax are the official way to do module loading in ECMAScript, which is the actual name for the JavaScript language specification.

You can - and should - also use this for CSS. Just import, then the path:

17 lines assets/js/app.js
... lines 1 - 7
// any CSS you require will output into a single css file (app.css in this case)
import '../css/app.css';
... lines 10 - 17

There's no from in this case because we don't need it to return a value to us.

Make sure all this coolness works: refresh! Yes!

Woh! Hey! Shut the front door! Did we just organize our JavaScript without global variables? Yes! We totally did! And that is no small thing. Heck, we could stop the tutorial right now, and you would still have this amazing superpower.

But... we won't! There is still so much cool stuff to talk about. Like, how we can now super easily install third-party libraries via Yarn and import them in our code. Let's do it!

Leave a comment!

20
Login or Register to join the conversation
Default user avatar
Default user avatar Robbert Baggio | posted 3 years ago

You are a hero! Nice video's my man!

1 Reply
CloudCreators Avatar
CloudCreators Avatar CloudCreators | posted 2 years ago

the .js and .css files are getting built in the build directory but console.log is not working it seems. No output on console after successfull build

Reply

Hey CloudCreators

Can you double-check that the CSS and JS files are being requested in your browser? You can check by opening the web debug toolbar and inspect the requests. A second thing to do is to do a hard refresh on your Browser (Usually Ctrl + F5)

Cheers!

Reply
CloudCreators Avatar
CloudCreators Avatar CloudCreators | MolloKhan | posted 2 years ago

Now I find this in my console suddenly :

GET https://127.0.0.1:8000/favicon.ico 404 not found

Reply

Hey Shubham,

It's minor, and you can completely ignore this error for now while develop a project for learning purposes, but it's bad for production of course. If you want to fix it - make sure you have that favicon.ico file in your public/ directory. If not - create an .ico file and put it there, and try again. Or, if you have that ico file in a different dir - point the link in your base.html.twig to it. But I'd not recommend you to place it in a different place or handle this file via Webpack Encore - this is something that should be called favicon.ico and be stored in the public/ dir, think of this as a defacto standard :)

Cheers!

Reply
CloudCreators Avatar
CloudCreators Avatar CloudCreators | MolloKhan | posted 2 years ago

MY APP.JS FILE

import '../styles/app.css';
import getNiceMessage from './get_nice_message';
console.log(getNiceMessage(5));
// start the Stimulus application
import '../bootstrap';

MY GETNICEMESSAGE.JS FILE

export default function(exclamationCount){
// console.log('Hello Webpack Encore! Edit me in assets/js/app.js'+'!'.repeat(exclamationCount));
return 'Hello Webpack Encore! Edit me in assets/js/app.js'+'!'.repeat(exclamationCount);
};

THE background color changes as specified in .css but no console log printed.

Reply
CloudCreators Avatar
CloudCreators Avatar CloudCreators | MolloKhan | posted 2 years ago

The css is working the background colour changes but no output on console which I put in my .js file. I tried the hard refresh too.

Reply
Nikolay S. Avatar
Nikolay S. Avatar Nikolay S. | posted 3 years ago

Is it possible to integrate the Material Components Web from material.io into Encore?

Reply

Hey Niki,

Yes, it should just work out of the box as soon as it works with Webpack. I see in their docs that to use the "@material/textfield" component you just need to import it as:


import {MDCTextField} from '@material/textfield/index';

So, just try to follow their installation steps in docs: https://github.com/material-components/material-components-web#using-npm

I hope this helps! If you have any problems with Encore during those steps - just let us know and we would try to help!

Cheers!

Reply
Nikolay S. Avatar
Nikolay S. Avatar Nikolay S. | Victor | posted 3 years ago

Okay thank you. Is there a way to use Angular Material only instead of Material Components in non-Angular App?

Reply

Hey Niki,

Well, I'm not familiar with Angular Material unfortunately, so don't know, probably better to just google this :)

Cheers!

Reply
Default user avatar
Default user avatar danslacabanedelilie | posted 4 years ago

Hello, first thanks a lot for this amazing course !!! I discover so much cool things ! But I have a problem, I can't import external jquery library.
I tried with jquery nicescroll and jquery circle progress but I have always an error $(...).niceScroll is not a function or $(...).circleProgress is not a function...
No problem for using jquery and no problem for using bootstrap functions like dropdown for example but other external libraries don't work :(

I installed it with yarn and imported it like that :

'use strict';
import $ from 'jquery';
import 'bootstrap';
import 'jquery.nicescroll';
import 'jquery-circle-progress';

// and next my code

// this code is ok

$(".side-navbar li a").click(function(event) {
$(".collapse").collapse('hide');
});

// this code makes an error nicescroll not a function

$(".sidebar-scroll").niceScroll({
// some options
});

Thanks a lot for your help

Reply

Hey danslacabanedelilie

That's a problem coming from bad written Jquery plugins. I recommend you to watch this episode where Ryan talks about it and shows a solution: https://symfonycasts.com/sc...

Cheers!

Reply
Default user avatar
Default user avatar danslacabanedelilie | MolloKhan | posted 4 years ago | edited

Thanks MolloKhan for your response. I've watched this episode and the autoProvidejQuery() is in my webpack.config.js but not better. And it's not a "jQuery is not defined" error that I have but it's $(...).niceScroll is not a function or $(...).circleProgress is not a function.
I don't understand why it's not working. Here is my config file :

var Encore = require('@symfony/webpack-encore');
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')

.addEntry('register', './assets/js/register.js')
.addEntry('default', './assets/js/default.js')
.addEntry('ecole', './assets/js/ecole.js')
.addEntry('projet', './assets/js/projet.js')

.splitEntryChunks()

.enableSingleRuntimeChunk()

.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())

.configureBabel(() => {}, {
useBuiltIns: 'usage',
corejs: 3
})

.enableLessLoader()

.autoProvidejQuery()

.copyFiles({
from: './assets/img',
to: 'img/[path][name].[hash:8].[ext]'
})
;

module.exports = Encore.getWebpackConfig();

Reply

Hey Danslacabanedelilie,

I see you have ".autoProvidejQuery()" in your webpack config - that's good! So, the question is how you import things in your JS files? IN you file where, e.g. you want to circleProgress plugin you need to import jQuery first, and then import the plugin, i.e. something like this:
import $ from 'jquery';<br />import 'jquery-circle-progress';

So, only import the jQuery is NOT enough, you have to also import the plugins you are going to use, and do it AFTER jQuery is imported. Also, it depends on plugins, you may need different workarounds for different plugins, I'd recommend you to look into official plugin repositories - search in docs or search for issues where people describe how to import the plugin with Webpack and do the same.

I hope this helps!

Cheers!

Reply
Default user avatar
Default user avatar danslacabanedelilie | Victor | posted 4 years ago | edited

Hello victor thanks for your response.
As I said in my first comment I installed it with yarn and imported it like that in my js file :

`
'use strict';
import $ from 'jquery';
import 'bootstrap';
import 'jquery.nicescroll';
import 'jquery-circle-progress';
import 'chart.js';

// and next my code

// this code is ok
$(".side-navbar li a").click(function(event) {
$(".collapse").collapse('hide');
});

// this code makes an error nicescroll not a function
$(".sidebar-scroll").niceScroll({
// some options
});

// this code makes an error circleprogress not a function
$('.circle').circleProgress({
value: 0.87,
size: 150,
startAngle: -Math.PI / 2,
thickness: 14,
lineCap: 'round',
emptyFill: '#f0eff4',
fill: {
gradient: ['#f9a58d', '#e76c90']
}
}).on('circle-animation-progress', function (event, progress) {
$(this).find('.percent').html(Math.round(87 * progress) + '<i>%</i>');
});
`

I also use chart.js and I have no problem with it but it's written in vanilla...
It seems it's only with jquery plugins, I've tried with some others and it's the same.
I looked into official repo for circle progress for example and it needs no specific configuration so I really don't understand...
I will open an issue in plugins repo maybe someone will know what's happen

Reply

Interesting... try adding this to your webpack.config.js file


...
const config = Encore.getWebpackConfig();

// see https://github.com/symfony/webpack-encore/issues/244
config.resolve.alias.jquery = path.join(__dirname, 'node_modules/jquery/dist/jquery');

module.exports = config;
// end of file
Reply
Default user avatar
Default user avatar danslacabanedelilie | MolloKhan | posted 3 years ago | edited

MolloKhan OMG you saved my life !!! I was on vacation, I just tried and magic it works now ! I will look at the issue to understand the trick, thanks thanks thanks !!!

Reply

#assets/app.js
import $ from 'jquery';
import 'carousel';
import 'bootstrap'; //adds functions to jquery

$(document).ready(function () {
var owl = $('#owl-slide');
owl.on('changed.owl.carousel', function (event) {
var element = event.target;
$('div[id="slide-shortcuts_"]').hide();

});
owl.owlCarousel({
items: 1,
nav: true,
autoplay: true,
autoplayHoverPause: true,
autoplayTimeout: 4000,
navText: [
"prev",
"next"
],
dotsContainer: '.owl-pagination'
});
});

It says to me owlcarousel is not a function also with pushmenu he tells me to install it with npm, I've done that and he says the same thing over over. I'm not sure if use encore or going back to the imports without it, it's so boring

Reply

Hey Giacomo,

Did you follow their quick start: https://github.com/OwlCarou... ? I see they say to import it as 'owl.carousel'.

Cheers!

Reply
Cat in space

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

This tutorial works great with Symfony5!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "aws/aws-sdk-php": "^3.87", // 3.91.4
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
        "knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
        "knplabs/knp-time-bundle": "^1.8", // 1.9.0
        "league/flysystem-aws-s3-v3": "^1.0", // 1.0.22
        "league/flysystem-cached-adapter": "^1.0", // 1.0.9
        "liip/imagine-bundle": "^2.1", // 2.1.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.1.0
        "oneup/flysystem-bundle": "^3.0", // 3.0.3
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.3.1
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.2.5
        "symfony/console": "^4.0", // v4.2.5
        "symfony/flex": "^1.9", // v1.17.6
        "symfony/form": "^4.0", // v4.2.5
        "symfony/framework-bundle": "^4.0", // v4.2.5
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/security-bundle": "^4.0", // v4.2.5
        "symfony/serializer-pack": "^1.0", // v1.0.2
        "symfony/twig-bundle": "^4.0", // v4.2.5
        "symfony/validator": "^4.0", // v4.2.5
        "symfony/web-server-bundle": "^4.0", // v4.2.5
        "symfony/webpack-encore-bundle": "^1.4", // v1.5.0
        "symfony/yaml": "^4.0", // v4.2.5
        "twig/extensions": "^1.5" // v1.5.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.1.0
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.7
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/debug-bundle": "^3.3|^4.0", // v4.2.5
        "symfony/dotenv": "^4.0", // v4.2.5
        "symfony/maker-bundle": "^1.0", // v1.11.5
        "symfony/monolog-bundle": "^3.0", // v3.3.1
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.2.5
        "symfony/profiler-pack": "^1.0", // v1.0.4
        "symfony/var-dumper": "^3.3|^4.0" // v4.2.5
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@symfony/webpack-encore": "^0.27.0", // 0.27.0
        "autocomplete.js": "^0.36.0",
        "autoprefixer": "^9.5.1", // 9.5.1
        "bootstrap": "^4.3.1", // 4.3.1
        "core-js": "^3.0.0", // 3.0.1
        "dropzone": "^5.5.1", // 5.5.1
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.4.0", // 3.4.0
        "popper.js": "^1.15.0",
        "postcss-loader": "^3.0.0", // 3.0.0
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^7.0.1", // 7.3.1
        "sortablejs": "^1.8.4", // 1.8.4
        "webpack-notifier": "^1.6.0" // 1.7.0
    }
}
userVoice