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
“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:
- 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.
- 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:
archive: 'dist/<%= pkg.name %>-<%= pkg.version %>.zip'
src: [ 'app/index.html' ],
src: [ '**' ],
// ... 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.