{"id":1683,"date":"2014-09-17T06:31:59","date_gmt":"2014-09-17T12:31:59","guid":{"rendered":"http:\/\/www.mooreds.com\/wordpress\/?p=1683"},"modified":"2014-08-30T06:32:51","modified_gmt":"2014-08-30T12:32:51","slug":"preparing-your-angularjs-app-for-deployment","status":"publish","type":"post","link":"https:\/\/www.mooreds.com\/wordpress\/archives\/1683","title":{"rendered":"Preparing Your AngularJS App for Deployment"},"content":{"rendered":"<figure style=\"width: 180px\" class=\"wp-caption alignleft\"><img decoding=\"async\" class=\"alignleft\" title=\"Angles, lines, light, and shadows by kevin dooley\" src=\"http:\/\/www.mooreds.com\/wordpress\/wp-content\/uploads\/2014\/08\/1905508309_8e9b7984da_m_angle.jpg\" alt=\"angle photo\" width=\"180\" \/><figcaption class=\"wp-caption-text\"><small>Photo by <a href=\"http:\/\/www.flickr.com\/photos\/12836528@N00\/1905508309\" target=\"_blank\">kevin dooley<\/a> <a title=\"Attribution License\" href=\"http:\/\/creativecommons.org\/licenses\/by\/2.0\/\" target=\"_blank\" rel=\"nofollow\"><img decoding=\"async\" src=\"http:\/\/www.mooreds.com\/wordpress\/wp-content\/plugins\/wp-inject\/images\/cc.png\" alt=\"\" \/><\/a><\/small><\/figcaption><\/figure>\n<p>I have recently been working on an AngularJS CRUD front end to a REST API (built with DropWizard).\u00a0 I have been working off the <a href=\"https:\/\/github.com\/angular\/angular-phonecat\">angular-phonecat<\/a> example app (from the <a href=\"https:\/\/docs.angularjs.org\/tutorial\">tutorial<\/a>).<\/p>\n<p>After making a few changes, I wanted to deploy the app to a standalone web server (Apache2).\u00a0 I naively checked out the codebase on the web server, and visited index.html.<\/p>\n<p>I saw a blank screen.\u00a0 Looking in the console, I saw this error message: <code>ReferenceError: angular is not defined<\/code><\/p>\n<p>Whoops.<\/p>\n<p>&#8220;Looks like there&#8217;s more to deploying this application than I thought.&#8221;\u00a0 Some searching around doesn&#8217;t reveal a lot, maybe because deployment is just taken for granted in the AngularJS community?\u00a0 Or I don&#8217;t know what questions to ask?<\/p>\n<p>Regardless, the fundamental fact of building AngularJS apps for deployment is that, at least with the angular-phonecat base, your dependencies are managed by <a href=\"http:\/\/bower.io\">bower<\/a> and\/or <a href=\"http:\/\/npmjs.org\">npm<\/a>, and you need to make sure you bundle them up when you are running on a web server that isn&#8217;t the npm started web server.<\/p>\n<p>In practice, this means writing a Gruntfile (or, actually, <a href=\"http:\/\/g00glen00b.be\/angular-grunt\/\">modify this Gruntfile<\/a>), which is similar to an <a href=\"http:\/\/ant.apache.org\/\">ant<\/a> build.xml file&#8211;you write targets and then gather them together.<\/p>\n<p>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.\u00a0 In the end, I had two tasks:<\/p>\n<ol>\n<li><a href=\"https:\/\/www.npmjs.org\/package\/grunt-bowercopy\">bowercopy<\/a> to\u00a0 copy my bower dependencies to a temp directory.\u00a0 I tried to use the <a href=\"https:\/\/www.npmjs.org\/package\/grunt-bower\">bower grunt task<\/a>, but wasn&#8217;t able to make it work.<\/li>\n<li><a href=\"https:\/\/www.npmjs.org\/package\/grunt-contrib-compress\">compress<\/a> to gather the files and build the zip file<\/li>\n<\/ol>\n<p>These were bundled together in a &#8216;package&#8217; task that looked like this: <code>grunt.registerTask('package', [ 'bowercopy', 'compress:dist' ]);<\/code><\/p>\n<p>The compress task is complicated and took some figuring out (<a href=\"http:\/\/www.charlestonsw.com\/what-i-learned-about-grunt-compress\/\">this post was helpful<\/a>, as was a close reading of the <a href=\"https:\/\/github.com\/gruntjs\/grunt-contrib-compress\">task&#8217;s readme page<\/a> and the page detailing how <a href=\"http:\/\/gruntjs.com\/configuring-tasks#building-the-files-object-dynamically\">file objects can be dynamically generated<\/a>). Here&#8217;s an example of the dist task:<\/p>\n<pre> compress: {\r\n          dist: {\r\n            options: {\r\n              archive: 'dist\/&lt;%= pkg.name %&gt;-&lt;%= pkg.version %&gt;.zip'\r\n            },\r\n            files: [{\r\n              src: [ 'app\/index.html' ],\r\n              dest: '\/',\r\n              expand: true,\r\n              flatten: true\r\n            }, {\r\n              cwd: 'dist\/libs',\r\n              src: [ '**' ],\r\n              dest: 'bower_components',\r\n\t      expand: true,\r\n            },\r\n              \/\/ ... more files definitions \r\n            ]\r\n          }\r\n  }<\/pre>\n<p>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).<\/p>\n<p>Then, the set of files to be added to this archive is specified. The <code>src<\/code> attribute outlines the list of files to include in the zip file. <code>src<\/code> handles wildcards (* for all in the directory, ** for all in the directory and subdirectories). The <code>dest<\/code> attribute, in contrast, indicates the directory where the file is to land in the zip archive. The <code>expand<\/code> attribute lets grunt do dynamic file matching (<a href=\"http:\/\/gruntjs.com\/configuring-tasks#building-the-files-object-dynamically\">more here<\/a>). The <code>flatten<\/code> attribute removes all the leading paths from the source files&#8211;if you didn&#8217;t have <code>flatten:true<\/code> in the <code>index.html<\/code> entry, it would be placed at <code>\/app\/index.html<\/code> in the zip file. The <code>dist\/libs<\/code> entry handles all the dependencies that were copied to that tmp directory by the <code>bowercopy<\/code> task. What the <code>cwd<\/code> attribute tells grunt is to act like it is in that directory for the <code>src<\/code> attribute. So, a file at <code>dist\/lib\/foo\/bar<\/code> will be treated like it was <code>foo\/bar<\/code> and, in the task above, copied to <code>bower_components\/foo\/bar<\/code> in the zipfile. This allows one to maintain the same directory structure in my <code>index.html<\/code> file in both dev and production.<\/p>\n<p>Finally, you need to install grunt and run <code>grunt package<\/code> to get your zipfile with all dependencies, for deployment.<\/p>\n<p>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 <a href=\"http:\/\/g00glen00b.be\/angular-grunt\/\">this post<\/a> for more information, but this was enough to get the app running in an environment without npm or any of the other angularJS toolchain.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have recently been working on an AngularJS CRUD front end to a REST API (built with DropWizard).\u00a0 I have been working off the angular-phonecat example app (from the tutorial). [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[67,54],"tags":[],"class_list":["post-1683","post","type-post","status-publish","format-standard","hentry","category-angularjs","category-javascript"],"_links":{"self":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/1683","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/comments?post=1683"}],"version-history":[{"count":5,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/1683\/revisions"}],"predecessor-version":[{"id":1692,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/posts\/1683\/revisions\/1692"}],"wp:attachment":[{"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1683"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1683"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mooreds.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1683"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}