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!

  • 2020-01-28 Robbert Baggio

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

  • 2020-01-02 Victor Bocharsky

    Hey Niki,

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

    Cheers!

  • 2019-12-30 Niki

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

  • 2019-12-30 Victor Bocharsky

    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...

    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!

  • 2019-12-30 Niki

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

  • 2019-09-18 danslacabanedelilie

    Diego Aguiar 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 !!!

  • 2019-08-14 Diego Aguiar

    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
  • 2019-08-14 danslacabanedelilie

    Hello Victor Bocharsky 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 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

  • 2019-08-14 Victor Bocharsky

    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';
    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!

  • 2019-08-13 danslacabanedelilie

    Thanks Diego Aguiar 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();

  • 2019-08-12 Diego Aguiar

    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!

  • 2019-08-12 danslacabanedelilie

    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

  • 2019-05-20 Victor Bocharsky

    Hey Giacomo,

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

    Cheers!

  • 2019-05-17 Giacomo Balloccu

    #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