Modular Javascript inside a Laravel 5 project

In our latest blog on best practices for merging and minifying your Javascript code, the recipe for doing this was not quite verbose. Therefore this blog will describe a typical approach of modular Javascript inside a Laravel project.

A side note on the Laravel version

In this blog we assume you are running Laravel 5, because a base install of the package Laravel Elixir is installed automatically and we will shortly see that this makes life much easier.

One entry point for Javascript

The key point of merging and minifying your Javascript code is to reduce the number of HTTP requests when the page loads. Therefore, we ideally want to have one script included in our page. In our main blade template main.blade.php (I assume this file to be in /resources/views, but you could of course use another name for it) we include the script app.js which is an automated build that is stored in the folder public/js. If you don’t have this file at this point, don’t worry, you will shortly.

We need some modules!

We need some modules to play around with during development. We put these modules in the folder resources/assets/js and create the main file app.js. We are not using one of the hot JS frameworks in this example, but we would like to use both Bootstrap (for some standard CSS and layout) and jQuery because it makes frontend development easy. For the moment I also assume you are using npm (we wrote a post about installing node and npm) to install your JS modules and that you have installed both Bootstrap and jQuery with npm.

To be able to use these modules in app.js we have to require them. The require keyword is used by the bundling tool (coming up in a minute) to manage the dependencies. Below the first two lines of app.js:

window.$ = window.jQuery = require('jquery');
require('bootstrap');

What happens here is a bit special: Since Bootstrap needs jQuery to be loaded on the window (aaik, a global variable!), we require jquery (Yes with a lower case q) and put it on the window object. Then we require bootstrap. All code we write below can now safely use either $ or jQuery and all the Bootstrap functionality, great!

Now assume we would like to have a datetime picker in our application. We would like to just add a class datetimepicker to our input fields and have this working automatically on all pages. Let’s build a module for that!

Create a new file in resources/assets/js and let’s call it my_datepicker.js. It contains the following code:

var AllPages = function($) {

	// ---
	// add a datepicker when there are date input fields
	// ---
	if ($('.datetimepicker').length) {
		require('moment');
		require('bootstrap-datetimepicker-build');

		// DATETIME-picker
		$('.datetimepicker').datetimepicker({
			format: 'YYYY-MM-DD HH:mm:ss'
		});
	}

}(jQuery);

Side note on using npm modules with require

In the code snippet above, we have two require statements. The argument is simply the name of the module that was installed with npm. I am not completely sure how this works, but just using the module name is enough to find the module and use it in your code.

We use the datetimepicker module for Bootstrap which is available on npm and install it by running npm install bootstrap-datetimepicker-build (note we also install moment.js since the datetimepicker depends on it. You can easily see which dependencies a npm module has on the npm website). The whole thing is wrapped in an anonymous function that is immediately executed and the result is put into the variable AllPages. But how can we make this work? By requiring this module in app.js, like so

require('./my_datepicker');

What about the ‘./’ you might wonder? This makes sure the module is looked up from the position in your directory structure it is required from. Basically, if you require a module within the same directory, you just use ‘./’. If the module is in a nested folder you use ‘./pathh/to/your/file/my_datepicker.js’.

Time to setup Gulp with Browserify

Our basic setup of the Javascript files is finished, it is time to setup a task runner to bundle the scripts, possibly minify them and copy the bundled script over to the public/js directory. With Laravel Elixir this task is very easy. Look in the root directory of your project and you will find a gulpfile.js. By default it probably looks like this:

var elixir = require('laravel-elixir');

/*
 |--------------------------------------------------------------------------
 | Elixir Asset Management
 |--------------------------------------------------------------------------
 |
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | for your Laravel application. By default, we are compiling the Less
 | file for our application, as well as publishing vendor resources.
 |
 */

elixir(function(mix) {
	mix.less('app.less', 'css/app.css');
});

which is quite self explanatory: the less files (stored in resources/assets/less) are compiled to a css file that the browser can read and the css file is stored in public/css.

But we need one extra line here to bundle our Javascript files:

mix.browserify('app.js');

This command does exactly what we want: it starts with our main app.js file in resources/assets/js and traverses all the dependencies we put in with require and bundles the result in a file app.js and puts this file in public/js. This is the default functionality. If you like the task to look for scripts in other places or store the resulting bundled file somewhere else, we have to include additional arguments to the browserify function, check the docs for all the details.

Running Gulp

Running the task runner is done by typing gulp in the command line in the root of your project. There are, however, two more commands worth explaining. Typing gulp –production will not only bundle, but also minify the result script. And gulp watch is a very handy command when you are developing. On saving one of your JS modules (all the way down to the ones you included with require), the bundling will be done again automatically!

Concluding remarks

With this recipe you can now start your own modular Javascript development inside a Laravel 5 project. However, there are some things I did not mention here that are worth thinking about. For example conditional loading of your modules (not all modules are required on all pages). Currently I have a series of if blocks in my app.js that check the URL of the page and requires specific modules on specific pages. This does not mean they are not loaded, it only means they are not executed. Another important thing if you are working with AJAX requests is to make sure you don’t get a token mismatch error when posting. To prevent this, use this little snippet (for example inside the module we just wrote as we need this on all pages!)

$(function() {
		$.ajaxSetup({
			headers: {
				'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
			}
		});
	});

and make sure to add the a meta tag to the head of your document with Laravel’s csrf_token as content!


Mijn Twitter profiel Mijn Facebook profiel
Pim Hooghiemstra Webdeveloper and founder of PLint-sites. Loves to build complex webapplications using Vue and Laravel! Latest post
Laravel-mix and the spread operator

2 thoughts on “Modular Javascript inside a Laravel 5 project

  1. drew

    This was very helpful. I liked this simple explanation of using laravel elixir, browserify, bootstrap, and datetimepicker. However, I get the following error:

    Uncaught TypeError: $(…).datetimepicker is not a function

    I’ve checked and double checked, and tried multiple fixes… but I can’t figure out what i’m doing wrong. Any input would be greatly appreciated.

    thanks
    drew

    Reply
    1. Pim Hooghiemstra Post author

      Drew,

      Thanks for your reply!

      I think I had this same problem, very frustrating indeed. When I looked into the issue in more detail I found that the eonasdan datetimepicker module available on NPM was for a specific packaged version of jQuery, namely jQuery 1 (if I remember correctly).

      Back in the day I found a fork of this repo but I can’t seem to find the link for it at the moment on NPM. If you would like to send me an e-mail on pim-at-plint-sites-dot-nl then I could send you the code I have used myself.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *