Skip to content

Preparing Your AngularJS App for Deployment

angle photo
Photo by kevin dooley

I have recently been working on an AngularJS CRUD front end to a REST API (built with DropWizard).  I have been working off the angular-phonecat example app (from the tutorial).

After making a few changes, I wanted to deploy the app to a standalone web server (Apache2).  I naively checked out the codebase on the web server, and visited index.html.

I saw a blank screen.  Looking in the console, I saw this error message: ReferenceError: angular is not defined

Whoops.

“Looks like there’s more to deploying this application than I thought.”  Some searching around doesn’t reveal a lot, maybe because deployment is just taken for granted in the AngularJS community?  Or I don’t know what questions to ask?

Regardless, the fundamental fact of building AngularJS apps for deployment is that, at least with the angular-phonecat base, your dependencies are managed by bower and/or npm, and you need to make sure you bundle them up when you are running on a web server that isn’t the npm started web server.

In practice, this means writing a Gruntfile (or, actually, modify this Gruntfile), which is similar to an ant build.xml file–you write targets and then gather them together.

For my initial Gruntfile, I wanted to make things as simple as possible, so I stripped out some of the fanciness of the g00glen00b file.  In the end, I had two tasks:

  1. bowercopy to  copy my bower dependencies to a temp directory.  I tried to use the bower grunt task, but wasn’t able to make it work.
  2. compress to gather the files and build the zip file

These were bundled together in a ‘package’ task that looked like this: grunt.registerTask('package', [ 'bowercopy', 'compress:dist' ]);

The compress task is complicated and took some figuring out (this post was helpful, as was a close reading of the task’s readme page and the page detailing how file objects can be dynamically generated). Here’s an example of the dist task:

 compress: {
          dist: {
            options: {
              archive: 'dist/<%= pkg.name %>-<%= pkg.version %>.zip'
            },
            files: [{
              src: [ 'app/index.html' ],
              dest: '/',
              expand: true,
              flatten: true
            }, {
              cwd: 'dist/libs',
              src: [ '**' ],
              dest: 'bower_components',
	      expand: true,
            },
              // ... more files definitions 
            ]
          }
  }

I want to unpack this a bit. First, the options object specifies an archive file and the name of the file. You can see that it is dynamically created based on values from the package.json (which is read in earlier in the grunt config file).

Then, the set of files to be added to this archive is specified. The src attribute outlines the list of files to include in the zip file. src handles wildcards (* for all in the directory, ** for all in the directory and subdirectories). The dest attribute, in contrast, indicates the directory where the file is to land in the zip archive. The expand attribute lets grunt do dynamic file matching (more here). The flatten attribute removes all the leading paths from the source files–if you didn’t have flatten:true in the index.html entry, it would be placed at /app/index.html in the zip file. The dist/libs entry handles all the dependencies that were copied to that tmp directory by the bowercopy task. What the cwd attribute tells grunt is to act like it is in that directory for the src attribute. So, a file at dist/lib/foo/bar will be treated like it was foo/bar and, in the task above, copied to bower_components/foo/bar in the zipfile. This allows one to maintain the same directory structure in my index.html file in both dev and production.

Finally, you need to install grunt and run grunt package to get your zipfile with all dependencies, for deployment.

There are a lot of other beneficial changes grunt can make to your app before deployment, like concatenating css and minifying the javascript, and I encourage you to read this post for more information, but this was enough to get the app running in an environment without npm or any of the other angularJS toolchain.

2 thoughts on “Preparing Your AngularJS App for Deployment

  1. g00glen00b says:

    Noticed the pingback, interesting blog. You earned yourself a new subscriber :p

  2. moore says:

    Thanks! I enjoyed your blog too.

Comments are closed.