Manually injecting Bower packages and AngularJS app files into index.html is a tedious job that a lot of us already do when developing an AngularJS app. We even waste more time when we need to remove or change name of those files. Fortunately, we can use Gulp to do task job for us. Just put some placeholders into our index.html and run a Gulp task and then you get the result similar to picture below:

Before and after inject files to index.html

Imagine that we have hundred of files, this tiny Gulp task will save us a lot of time.

Here are somethings we’re going to do with Gulp in this article:

  • Automatic inject bower components.
  • Automatic inject AngularJS files.
  • Setup a development server and reload browser when file changes detected.

Our demo project is very simple, it’s just have 1 module and display a welcome text on website. You can quickly grab the source code of this project at here: https://github.com/davidtran/davidtran.github.io/tree/master/gulp_practice. In order to run demo, you need to install gulp and bower first.

Let’s get started

Automatic inject bower components:

We can use wiredep to automatic locate files from bower’s packages and inject them to index.html. Wiredep does it by gather files from “main” and “dependencies” properties in bower.json of each packages in bower_componets folder, after that replace with those files with placeholder in index.html. So in order to use wiredep, we need to put a placeholder in our index.html:

<!-- bower:js -->
<!-- endbower -->

And configure wiredep in gulpfile:

[javascript]
‘use strict’;

var gulp = require(‘gulp’);
var $ = require(‘gulp-load-plugins’)();
var wiredep = require(‘wiredep’).stream;
gulp.task(‘inject’, function() {

var wiredepOptions = {
directory: ‘public/components’,
exclude: [/bootstrap-sass-official/, /bootstrap\.css/]
};

return gulp.src(‘src/*.html’)
.pipe(wiredep(wiredepOptions))
.pipe(gulp.dest(‘build’));
});

[/javascript]

Upon gather files from bower_components, wiredep will replace the placeholder by reference to js files.

That’s need to said that wiredep will fail to work if a package doesn’t have “dependencies” and “main” property in its bower.json, in this case, you need to override these property. See how to do it in this article: https://github.com/taptapship/wiredep#bower-overrides. Also because of this behavior wiredep can’t not inject css files from bower packages, you need to do it by yourself.

Automatic inject AngularJS app files:

An AngularJS project can easily have hundred of files and it’s boring to manually inject all of them to our index.html by yourself. Fortunately, we can use gulp-inject and gulp-angular-filesort to automatic inject them to index.html.

  • Gulp-inject take a stream of files and replace the placeholder in index.html with a list reference to our files. We can use gulp-inject for many other files files as well, depend on filetype, it will generate the corresponding tags. For example, if we provide css file, it will generate <link> tag.
  • Gulp-angular-filesort help us sort the Angular files depend on module definitions and usage before gulp-inject put them into index.html.

Here is our we do it in our project. Put this gulp-inject’s placeholder in index.html:

<!-- inject:js -->
<!-- endinject -->

Configure gulp-inject and gulp-angular-filesort in gulpfile.js.

[javascript]
var inject = require(‘gulp-inject’);
var angularFilesort = require(‘gulp-angular-filesort’);

gulp.task(‘inject’, function() {
var injectFiles = gulp.src(‘src/app/**/*.js’).pipe(angularFilesort());
var injectOptions = {
ignorePath: [‘src’]
};

//—–

return gulp.src(‘src/*.html’)
.pipe(inject(injectFiles))
.pipe(wiredep(wiredepOptions))
.pipe(gulp.dest(‘build’));
});

[/javascript]

At this point, gulp is ready to automatic inject bower components and our AngularJS app files into index.html. Let’s take a further step by reloading our browser upon file changes.

Create a web server to serve our AngularJS project and reload browser when file changes detected:

We can use browser-sync to create a development server for our app. There are many alternatives like livereload, but I choose browser-sync because it supports multiple browsers at once.

Now, we create a new gulp task to use brower-sync:

[javascript]

var browserSync = require(‘browser-sync’);
var browserSyncSpa = require(‘browser-sync-spa’);

gulp.task(‘live’, function() {

browserSync.use(browserSyncSpa({
selector: ‘[ng-app]’
}));

browserSync.init({
server: {
baseDir: [‘build’],
routes: {
‘/public/components’: ‘public/components’,
‘/src’: ‘src’
}
},
browser: ‘src’,
startPath: ‘/’
});
});

Because "public/components" and "src" folders stay outside of the base "build" folder, we have to use baseDir.routes to tell browser-sync the folder to serve files.

[/javascript]

Next, we use gulp.watch to watch for files changes and reload browser when file changes detected.

[javascript]

gulp.task(‘live’, function() {
browserSync.use(browserSyncSpa({
selector: ‘[ng-app]’
}));

gulp.watch(‘src/app/**/*.js’, function(event) {
gulp.start(‘inject’);
});

gulp.watch(‘src/app/**/*.html’, function(event) {
browserSync.reload(event.path);
});

gulp.watch([‘src/*.html’, ‘bower.json’], [‘inject’]);
browserSync.init({
server: {
baseDir: [‘build’],
routes: {
‘/public/components’: ‘public/components’,
‘/src’: ‘src’
}
},
browser: ‘src’,
startPath: ‘/’
});
});

[/javascript]

At the “inject” task, we also need to add browserSync.stream() when this task is going to finish.

[javascript]

gulp.task(‘inject’, function() {

return gulp.src(‘src/*.html’)
.pipe(inject(injectFiles))
.pipe(wiredep(wiredepOptions))
.pipe(gulp.dest(‘build’))
.pipe(browserSync.stream());
});

[/javascript]

Put it all together:

This is our index.html and gulpfile.js look like after we have finish configuring our plugins:

Content of index.html

<html ng-app="app">
	<head>
		<title>Pratice Gulp</title>

		<!-- inject:css -->
		<!-- endinject -->
	</head>
	<body ng-controller="appCtrl">



<div class="well">{{welcome}}</div>




		<!-- bower:js -->
		<!-- endbower -->

		<!-- inject:js -->
		<!-- endinject -->

	</body>
</html>

Content of gulpfile.js:

[javascript]

‘use strict’;

var gulp = require(‘gulp’);
var inject = require(‘gulp-inject’);
var angularFilesort = require(‘gulp-angular-filesort’);
var wiredep = require(‘wiredep’).stream;
var browserSync = require(‘browser-sync’);
var browserSyncSpa = require(‘browser-sync-spa’);

gulp.task(‘inject’, function() {
var injectFiles = gulp.src(‘src/app/**/*.js’).pipe(angularFilesort());
var injectOptions = {
ignorePath: [‘src’]
};

var wiredepOptions = {
directory: ‘public/components’,
exclude: [/bootstrap-sass-official/, /bootstrap\.css/]
};

return gulp.src(‘src/*.html’)
.pipe(inject(injectFiles))
.pipe(wiredep(wiredepOptions))
.pipe(gulp.dest(‘build’))
.pipe(browserSync.stream());
});

gulp.task(‘live’, function() {

browserSync.use(browserSyncSpa({
selector: ‘[ng-app]’
}));

gulp.watch(‘src/app/**/*.js’, function(event) {
if (event.type === ‘changed’) {
gulp.start(‘inject’);
}
});

gulp.watch(‘src/app/**/*.html’, function(event) {
browserSync.reload(event.path);
});

gulp.watch([‘src/*.html’, ‘bower.json’], [‘inject’]);

browserSync.init({
server: {
baseDir: [‘build’],
routes: {
‘/public/components’: ‘public/components’,
‘/src’: ‘src’
}
},
browser: ‘src’,
startPath: ‘/’
});
});

[/javascript]

From now we no longer waste time for inject javascript files into our project by ourselves anymore.

Happy coding !