One of the most painstaking aspects of web development is the extra step, one needs to do to see the recently made changes in the browser. ( i.e. with a manual refresh). Imagine if we have to test the website in multiple browsers. So, every developer would love to ease this process by having a live reloading of the pages in the browsers – that is as and when any changes are done to html/js/css files in the project and saved, the browser automatically reloads the new content. This is exactly what we are going to address in this post. To follow through this, some basic understanding of node.js, npm packages and grunt.js is desirable. In this post, I will use the angular-seed-project as a sample codebase for which we would be adding live-reload feature using grunt. The instructions to download and set the seed project is available in the read me file under the same project.
The below are the steps involved in achieving our objective of adding grunt tasks for live reload
- Open Terminal (In Mac)/ Command prompt in admin mode (Windows) and Navigate to the root directory of your web application
- Create a package.json file (this file would be used by npm to manage all the dependencies) under the root directory. Refer to one of my previous posts to create a new package.json file [Note : With the angular seed project this file would be present already in the expected location]
- Run the below command to install grunt-cli package globally. In Mac use `sudo` to avoid any permission issues
npm install -g grunt-cli
- We would be using the readily available grunt tasks grunt-contrib-watch and grunt-contrib-connect to enable the live reloading. Run the below command to install these grunt plugins locally to the current project. Watch task will continuously watch for any changes in the directory as configured and connect will serve the application with the specified configuration
npm install grunt --save-dev npm install grunt-contrib-watch --save-dev npm install grunt-contrib-connect --save-dev
After adding these packages, the package.json of angular-seed-project would look like this. If you are applying these changes on your existing web application, your package.json would look little different.
"devDependencies": { "bower": "^1.3.1", "grunt": "^0.4.5", "grunt-contrib-connect": "^0.10.1", "grunt-contrib-watch": "^0.6.1", "http-server": "^0.6.1", "karma": "~0.10", "karma-junit-reporter": "^0.2.2", "protractor": "^1.1.1", "shelljs": "^0.2.6" }
- Final step is to create the actual grunt tasks that does the operation. For this, we need to create a new Gruntfile.js file under the root directory. More on creating grunt tasks are available in grunt’s documentation. The finished gruntfile will look like the one below:
module.exports = function (grunt){ grunt.initConfig({ //watches for the specified files and executes the task associated to be performed watch : { refresh : { options :{ livereload: '<%= connect.options.livereload %>' }, files : ['app/**/*.js','app/**/*.css','app/**/*.html'] //give the list of all files that you want to watch for and reload } }, //serves the application under this configuration connect : { options: { port: 9000, // Change this to '0.0.0.0' to access the server from outside. hostname: 'localhost', livereload: 35729 }, livereload : { options: { open: true, } } } }); //load all Npm tasks grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-connect'); //register tasks to be run grunt.registerTask('serve', ['connect:livereload','watch']); }
Under the Watch task, mention all the files that you want to watch for, and perform the live reload. More advanced options about this plugin is available in grunt-contrib-watch documentation.
Connect task is just serving the page under a node server, the livereload option for watch is set to use this task’s options for the port configuration for livereload. To run the application, run the below command
grunt serve
Now, if you save any changes to html/js/css files present under ‘app’ folder, the application would get auto refreshed in all the browsers its opened. Sample output of the watch task would look as follows:
Running "watch" task Waiting... Verifying property watch exists in config...OK Verifying property watch.refresh.files exists in config...OK Live reload server started on port: 35729 Watching app\app.js for changes. Watching app\bower_components for changes. Watching app\components for changes. Watching app\view1 for changes. Watching app\view2 for changes. Watching app\app.css for changes. Watching app\index-async.html for changes. Watching app\index.html for changes. Watching app\bower_components\angular-loader\angular-loader.js for changes. Watching app\bower_components\angular-loader\angular-loader.min.js for changes. Watching app\bower_components\angular-mocks\angular-mocks.js for changes. Watching app\bower_components\angular-route\angular-route.js for changes. Watching app\bower_components\angular-route\angular-route.min.js for changes. Watching app\bower_components\angular\angular.js for changes. Watching app\bower_components\angular\angular.min.js for changes. Watching app\bower_components\angular\angular-csp.css for changes. Watching app\bower_components\html5-boilerplate\js\main.js for changes. Watching app\bower_components\html5-boilerplate\js\vendor for changes. Watching app\bower_components\html5-boilerplate\js\plugins.js for changes. Watching app\bower_components\html5-boilerplate\js\vendor\jquery-1.10.2.min.js for changes. Watching app\bower_components\html5-boilerplate\js\vendor\modernizr-2.6.2.min.js for changes. Watching app\components\version\interpolate-filter.js for changes. Watching app\components\version\interpolate-filter_test.js for changes. Watching app\components\version\version-directive.js for changes. Watching app\components\version\version-directive_test.js for changes. Watching app\components\version\version.js for changes. Watching app\components\version\version_test.js for changes. Watching app\view1\view1.js for changes. Watching app\view1\view1_test.js for changes. Watching app\view1\view1.html for changes. Watching app\view2\view2.js for changes. Watching app\view2\view2_test.js for changes. Watching app\view2\view2.html for changes. Watching app\bower_components\html5-boilerplate\css\main.css for changes. Watching app\bower_components\html5-boilerplate\css\normalize.css for changes. Watching app\bower_components\html5-boilerplate\404.html for changes. Watching app\bower_components\html5-boilerplate\css for changes. Watching app\bower_components\html5-boilerplate\doc for changes. Watching app\bower_components\html5-boilerplate\img for changes. Watching app\bower_components\html5-boilerplate\js for changes. Watching app\bower_components\html5-boilerplate\index.html for changes. <strong>>> File "app\view2\view2.html" changed.</strong> <strong>Live reloading app\view2\view2.html...</strong> <strong>Completed in 0.000s at Wed Apr 22 2015 23:02:04 GMT+0530 (India Standard Time) - Waiting...</strong>
Notice the last 3 lines, which captured the change in one of the files and did the reload of the application.
That’s it :-). Mission accomplished !!
As a word of caution, please be mindful of the files that you want to watch for (preferably limit the files to watch to the ones that you would modify as a part of your application changes), otherwise watch could lead to some memory issues, if it starts watching huge number of files.
Other equivalent options for livereload
- There are some browser extensions available, which does the action without any single line of code changes to the application. (I haven’t really tried any of those yet)
- If you are using Visual Studio 2013 as your IDE, you can make use of a feature called Browser Link, that enables browser refresh with a click of a button.
- If you are starting a new web application project, you should highly consider using scaffolding tool like Yeoman, that brings most of the required grunt, bower, travis and other necessary configurations by default
- If you are starting out hybrid mobile app development, you should consider using ionic framework, which comprises a powerful CLI with live reloading options enabled not just for browsers but also for ionic apps running in ios/android emulators. (Refer my previous posts (Part 1 & Part 2)for getting started with ionic )
I would be more interested to know if you have used/known of any other technique to enable live reloading of the application.