Angular.JS

and Yeoman



Using Yeoman to quickly build
an angular.js application



Matthew General
@mattgen88

Technologies


Yeoman                             A scaffolding utility to generate boilerplate

Angularjs           A Google project that does seemingly everything

Bower                                                     A javascript dependency manager

Npm                     A  javascript (node) dependency manager, as well

Grunt                                                                             Like Make, but javascript

Environment

Prerequisite:
Install nodejs with npm!

Install yeoman:
sudo npm -g install yo

You now have yeoman, grunt, bower, etc

Install angular generator:
sudo npm -g install generator-angular

You now have the angularjs generator for yeoman!

Creating your first project

$> yo
[?] 'Allo Matthew! What would you like to do? Run the Angular generator (0.9.0)
Make sure you are in the directory you want to scaffold into.
This generator can also be run with: yo angular
     _-----_
    |       |    .--------------------------.
    |--(o)--|    |    Welcome to Yeoman,    |
   `---------´   |   ladies and gentlemen!  |
    ( _´U`_ )    '--------------------------'
    /___A___\    
     |  ~  |     
   __'.___.'__   
 ´   `  |° ´ Y ` 
Out of the box I include Bootstrap and some AngularJS recommended modules.
[?] Would you like to use Sass (with Compass)? Yes
[?] Would you like to include Bootstrap? Yes
[?] Would you like to use the Sass version of Bootstrap? Yes
[?] Which modules would you like to include? (Press <space> to select)
‣⬢ angular-animate.js
 ⬢ angular-cookies.js
 ⬢ angular-resource.js
 ⬢ angular-route.js
 ⬢ angular-sanitize.js
 ⬢ angular-touch.js

Our workspace

$ls
app/  bower_components/  bower.json  Gruntfile.js  node_modules/  npm-debug.log  package.json  test/
$tree app
app
├── 404.html
├── favicon.ico
├── images
│   └── yeoman.png
├── index.html
├── robots.txt
├── scripts
│   ├── app.js
│   └── controllers
│       ├── about.js
│       └── main.js
├── styles
│   └── main.scss
└── views
    ├── about.html
    └── main.html

5 directories, 11 files

Let's build!

This creates a lot of output
It runs jshint, tests (karma), sass compilation, manages your javascript file inclusion, minifies javascript, concatentates javascript, CDNifies scripts, compresses images, revisions assets... wow that's a lot! Amazingly, we didn't have to set up a single one of these ourselves!
$> grunt
Running "newer:jshint" (newer) task

Running "newer:jshint:all" (newer) task
No newer files to process.

Running "newer:jshint:test" (newer) task
No newer files to process.

Running "clean:server" (clean) task
Cleaning .tmp...OK

Running "concurrent:test" (concurrent) task
    
    Running "compass:dist" (compass) task
    directory .tmp/styles/ 
       create .tmp/styles/main.css (0.024s)
    Compilation took 0.06s
    
    Running "compass:server" (compass) task
    unchanged app/styles/main.scss
    Compilation took 0.002s
    
    Done, without errors.
    
    
    Execution Time (2014-06-21 02:33:34 UTC)
    compass:dist    685ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 64%
    compass:server  388ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 36%
    Total 1.1s
    
Running "autoprefixer:dist" (autoprefixer) task
File .tmp/styles/main.css created.

Running "connect:test" (connect) task
Started connect web server on http://localhost:9001

Running "karma:unit" (karma) task
WARN [karma]: Port 8080 in use
WARN [karma]: Port 8081 in use
INFO [karma]: Karma v0.12.16 server started at http://localhost:8082/
INFO [launcher]: Starting browser PhantomJS
WARN [watcher]: Pattern "/srv/faq/test/mock/**/*.js" does not match any file.
INFO [PhantomJS 1.9.7 (Linux)]: Connected on socket R02YDLcekOs04-sTRZau with id 39748897
PhantomJS 1.9.7 (Linux): Executed 2 of 2 SUCCESS (0.04 secs / 0.022 secs)

Running "clean:dist" (clean) task
Cleaning .tmp...OK

Running "wiredep:app" (wiredep) task

Running "wiredep:sass" (wiredep) task
app/styles/main.scss modified.

Running "useminPrepare:html" (useminPrepare) task
Going through app/index.html to update the config
Looking for build script HTML comment blocks

Configuration is now:

  concat:
  { generated: 
   { files: 
      [ { dest: '.tmp/concat/scripts/vendor.js',
          src: 
           [ 'bower_components/jquery/dist/jquery.js',
             'bower_components/angular/angular.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/affix.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/alert.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/button.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/carousel.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/collapse.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/dropdown.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/tab.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/transition.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/scrollspy.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/modal.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/tooltip.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/popover.js',
             'bower_components/angular-resource/angular-resource.js',
             'bower_components/angular-cookies/angular-cookies.js',
             'bower_components/angular-sanitize/angular-sanitize.js',
             'bower_components/angular-animate/angular-animate.js',
             'bower_components/angular-touch/angular-touch.js',
             'bower_components/angular-route/angular-route.js' ] },
        { dest: '.tmp/concat/scripts/scripts.js',
          src: 
           [ '{.tmp,app}/scripts/app.js',
             '{.tmp,app}/scripts/controllers/main.js',
             '{.tmp,app}/scripts/controllers/about.js' ] } ] } }

  uglify:
  { generated: 
   { files: 
      [ { dest: 'dist/scripts/vendor.js',
          src: [ '.tmp/concat/scripts/vendor.js' ] },
        { dest: 'dist/scripts/scripts.js',
          src: [ '.tmp/concat/scripts/scripts.js' ] } ] } }

  cssmin:
  { generated: 
   { files: 
      [ { dest: 'dist/styles/vendor.css', src: [] },
        { dest: 'dist/styles/main.css',
          src: [ '.tmp/styles/main.css' ] } ] } }

Running "concurrent:dist" (concurrent) task
>> Warning: There are more tasks than your concurrency limit. After this limit
>> is reached no further tasks will be run until the current tasks are
>> completed. You can adjust the limit in the concurrent task options
    
    Running "imagemin:dist" (imagemin) task
    ✔ app/images/yeoman.png (saved 5 kB - 37%)
    Minified 1 image (saved 5 kB)
    
    Done, without errors.
    
    
    Execution Time (2014-06-21 02:33:53 UTC)
    loading tasks   12ms  ▇ 2%
    imagemin:dist  559ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 98%
    Total 573ms
        
    Running "svgmin:dist" (svgmin) task
    Total saved: 0 B
    
    Done, without errors.
    
    
    Execution Time (2014-06-21 02:33:59 UTC)
    svgmin:dist  792ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 99%
    Total 797ms
        
    Running "compass:dist" (compass) task
    directory .tmp/styles/ 
       create .tmp/styles/main.css (6.137s)
    Compilation took 6.182s
    
    Done, without errors.
    
    
    Execution Time (2014-06-21 02:33:53 UTC)
    compass:dist  9.7s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 100%
    Total 9.7s
    
Running "autoprefixer:dist" (autoprefixer) task
File .tmp/styles/main.css created.

Running "concat:generated" (concat) task
File .tmp/concat/scripts/vendor.js created.
File .tmp/concat/scripts/scripts.js created.

Running "ngmin:dist" (ngmin) task
ngminifying .tmp/concat/scripts/scripts.js, .tmp/concat/scripts/vendor.js

Running "copy:dist" (copy) task
Copied 11 files

Running "cdnify:dist" (cdnify) task
Going through dist/404.html, dist/index.html to update script refs

Running "cssmin:generated" (cssmin) task
>> Destination not written because minified CSS was empty.
File dist/styles/main.css created: 283.65 kB → 104.35 kB

Running "uglify:generated" (uglify) task
File dist/scripts/vendor.js created: 1.24 MB → 262.8 kB
File dist/scripts/scripts.js created: 1.19 kB → 576 B

Running "filerev:dist" (filerev) task
✔ dist/scripts/scripts.js changed to scripts.673b06a2.js
✔ dist/scripts/vendor.js changed to vendor.4c4a35b4.js
✔ dist/styles/main.css changed to main.e40f8f97.css
✔ dist/images/yeoman.png changed to yeoman.5cff96e1.png

Running "usemin:html" (usemin) task

Processing as HTML - dist/404.html
Update the HTML to reference our concat/min/revved script files
Update the HTML with the new css filenames
Update the HTML with the new img filenames
Update the HTML with the new video filenames
Update the HTML with the new poster filenames
Update the HTML with the new source filenames
Update the HTML with data-main tags
Update the HTML with data-* tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input
Update the HTML with the new img filenames in meta tags

Processing as HTML - dist/index.html
Update the HTML to reference our concat/min/revved script files
<script src="scripts/vendor.js" changed to <script src="scripts/vendor.4c4a35b4.js"
<script src="scripts/scripts.js" changed to <script src="scripts/scripts.673b06a2.js"
Update the HTML with the new css filenames
<link rel="stylesheet" href="styles/main.css" changed to <link rel="stylesheet" href="styles/main.e40f8f97.css"
Update the HTML with the new img filenames
Update the HTML with the new video filenames
Update the HTML with the new poster filenames
Update the HTML with the new source filenames
Update the HTML with data-main tags
Update the HTML with data-* tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input
Update the HTML with the new img filenames in meta tags

Processing as HTML - dist/views/about.html
Update the HTML to reference our concat/min/revved script files
Update the HTML with the new css filenames
Update the HTML with the new img filenames
Update the HTML with the new video filenames
Update the HTML with the new poster filenames
Update the HTML with the new source filenames
Update the HTML with data-main tags
Update the HTML with data-* tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input
Update the HTML with the new img filenames in meta tags

Processing as HTML - dist/views/main.html
Update the HTML to reference our concat/min/revved script files
Update the HTML with the new css filenames
Update the HTML with the new img filenames
<img src="images/yeoman.png" changed to <img src="images/yeoman.5cff96e1.png"
Update the HTML with the new video filenames
Update the HTML with the new poster filenames
Update the HTML with the new source filenames
Update the HTML with data-main tags
Update the HTML with data-* tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input
Update the HTML with the new img filenames in meta tags

Running "usemin:css" (usemin) task

Processing as CSS - dist/styles/main.e40f8f97.css
Update the CSS to reference our revved images

Running "htmlmin:dist" (htmlmin) task
Minified dist/404.html 3.53 kB → 3.39 kB
Minified dist/index.html 1.94 kB → 1.73 kB
Minified dist/views/about.html 31 B → 30 B
Minified dist/views/main.html 700 B → 656 B

Done, without errors.


Execution Time (2014-06-21 02:33:31 UTC)
concurrent:test    3.8s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 7%
karma:unit         4.5s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 8%
wiredep:app        5.8s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 11%
concurrent:dist   17.6s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 33%
ngmin:dist        15.2s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 28%
cdnify:dist        1.8s  ▇▇▇▇▇▇▇ 3%
uglify:generated   3.7s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 7%
Total 53.5s


Let's test it out!

We'll let grunt spin up an instance and let it open our browser for us. A dev could really get used to this.

Oh, and it has live reload so as we work, it'll refresh automagically.
$grunt serve
Running "serve" task

Running "clean:server" (clean) task
Cleaning .tmp...OK

Running "wiredep:app" (wiredep) task

Running "wiredep:sass" (wiredep) task

Running "concurrent:server" (concurrent) task
    
    Running "compass:server" (compass) task
    directory .tmp/styles/ 
       create .tmp/styles/main.css (2.517s)
    Compilation took 2.595s
    
    Done, without errors.
    
    
    Execution Time (2014-06-21 02:40:15 UTC)
    compass:server  3.4s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 100%
    Total 3.4s
    
Running "autoprefixer:dist" (autoprefixer) task
File .tmp/styles/main.css created.

Running "connect:livereload" (connect) task
Started connect web server on http://localhost:9000

Running "watch" task
Waiting...

Congratulations


You now have an app.

My work here is done, right?.... right?

Fine, fine...

Overview

  • Two way data binding. Models can be attached to user input and modification can go both ways.
  • You can define your own "elements"
  • Frontend routing, url's represent state
  • Templating system is simple yet powerful
  • Dependency Injection system (Freaking Magic TM)
  • Controllers, Services, Service Factories, Service Providers, and Directives

angularjs ROUTES

Routes take a URL and map them to a controller.
The controller contains a view to display the user
The view can consist of other controllers or directives or even just be elements for the user to interact with
Routes can be set up to accept parameters as well, which can be retrieved in a controller via route parameters.

ANGULARJS routes


$yo angular:route faq invoke angular:controller:/usr/lib/node_modules/generator-angular/route/index.js create app/scripts/controllers/faq.js create test/spec/controllers/faq.js invoke angular:view:/usr/lib/node_modules/generator-angular/route/index.js create app/views/faq.html
creates a route, creates a controller, creates a view for that controller.
We can then customize these to do what we need.

angularjs routes

$ vim app/scripts/app.js
  30       .when('/faq/:id', {                                                           
 31         templateUrl: 'views/faq/entry.html',                                          
 32         controller: 'FaqEntryCtrl'                                                    
 33       })
 34        .when('/faq', {
 35         templateUrl: 'views/faq.html',
 36         controller: 'FaqCtrl'
 37       })
When the browser's URL is #/faq
Load the template located at views/faq.html
Load the controller FaqCtrl for that view
RoutParams (:id) are accessible in a controller, allow with url parameters.

Controllers

Controllers tend to be where you define all of your functions for a given scope. Scopes contain the models/data bindings and functions. 
Used to define interactions, update and manipulate template expressions.

I tend to think of these as "Page controllers," most are bare bones and include a template with directives (which can have their own controllers)

CONTROLLERS

  1 'use strict';                                                                    
  2                                                                                  
  3 /**                                                                              
  4  * @ngdoc function                                                               
  5  * @name faqApp.controller:FaqEntryCtrl                                             
  6  * @description                                                                  
  7  * # FaqEntryCtrl                                                                   
  8  * Controller of the faqApp                                                      
  9  */                                                                              
 10 angular.module('faqApp')                                                         
 11   .controller('FaqEntryCtrl', function ($scope) {                                
 12     $scope.doSomething = function(){};
 13     $scope.someValue = ['foo', 'bar'];
 14     $scope.manipulateValue = function() {
 15       $scope.someValue.pop();
 16     };                                                                   
 17   });    

Controllers

$yo angular:controller helloWorld   create app/scripts/controllers/helloworld.js   create test/spec/controllers/helloworld.js
  1 'use strict';                                                                    
  2                                                                                  
  3 /**                                                                              
  4  * @ngdoc function                                                               
  5  * @name faqApp.controller:HelloworldCtrl                                        
  6  * @description                                                                  
  7  * # HelloworldCtrl                                                              
  8  * Controller of the faqApp                                                      
  9  */                                                                              
 10 angular.module('faqApp')                                                         
 11   .controller('HelloworldCtrl', function ($scope) {                              
 12     $scope.awesomeThings = [                                                     
 13       'HTML5 Boilerplate',                                                       
 14       'AngularJS',                                                               
 15       'Karma'                                                                    
 16     ];                                                                           
 17   });                                                                                                               

angularjs views

Templates that contain display logic and tokens
synced to the client and expanded on the frontend
Simple syntax, but full featured

Angularjs Views

<section ng-repeat="entry in entries" class="panel panel-default">               
 <div class="panel-heading"><a ng-click="viewPost(entry.slug)">{{entry.title}}</a></div>
 <div class="panel-body" btf-markdown="entry.body | characters:250: false"></div>
 <div class="panel-footer">                                                     
     <span class="author">Author: {{entry.author}}</span>                       
     <span class="created">Created on: {{entry.created.$date | date:'medium'}}</span>
     <span class="edited" ng-show="{{entry.edited.$date}}">Edited on: {{entry.edited.$date | date:'medium'}}</span>
 </div>                                                                         
</section>   
  • ng-repeat is a loop structure, duplicates element defined on
  • ng-click executes function on scope when clicked
  • {{entry.title}} displays model value of entry.title
  • btf-markdown is a custom directive for processing markdown
  • {{entry.created.$date | date: 'medium'}} filters a date into a different look
  • ng-show display element when 

Angularjs Services

Services are used when you need something shared between multiple controllers
Usually facilitates inter-controller/directive communication of some sort
Services are singleton
Three types: Service, Provider, Factory
Providers are fully configurable
Factories return the value of the function defined
Service is a very simple object

Angularjs service

Service:
 myApp.service('helloWorldFromService', function() {
    this.sayHello = function() {
        return "Hello, World!"
    };
});

Factory:
myApp.factory('helloWorldFromFactory', function() {
    return {
        sayHello: function() {
            return "Hello, World!"
        }
    };
});  

angularjs service

Provider:
 myApp.provider('helloWorld', function() {
    // In the provider function, you cannot inject any
    // service or factory. This can only be done at the
    // "$get" method.
 
    this.name = 'Default';
 
    this.$get = function() {
        var name = this.name;
        return {
            sayHello: function() {
                return "Hello, " + name + "!"
            }
        }
    };
 
    this.setName = function(name) {
        this.name = name;
    };
});
https://gist.github.com/Mithrandir0x/3639232

Angularjs directive

A directive is the encapsulation of DOM and scripts to manipulate that DOM. It exists as a user-defined 'tag' for use in angularjs templates. They are replaced with real DOM that we define. This is similar to shadow DOM.

$>yo angular:directive hello-world
   create app/scripts/directives/hello-world.js
   create test/spec/directives/hello-world.js

This generated our javascript for our directive and a test for it

angularjs directive (cont)

$> vim app/scripts/directives/hello-world.js
  1 'use strict';                                                                    
  2                                                                                  
  3 /**                                                                              
  4  * @ngdoc directive                                                              
  5  * @name faqApp.directive:helloWorld                                             
  6  * @description                                                                  
  7  * # helloWorld                                                                  
  8  */                                                                              
  9 angular.module('faqApp')                                                         
 10   .directive('helloWorld', function () {                                         
 11     return {                                                                     
 12       template: '<div></div>',                                                   
 13       restrict: 'E',                                                             
 14       link: function postLink(scope, element, attrs) {                           
 15         element.text('this is the helloWorld directive');                        
 16       }                                                                          
 17     };                                                                           
 18   });      

angularjs directive

  9  angular.module('faqApp')
Defines that this is part of the faqApp module
 10  .directive('helloWorld', function () { 
Defines a directive called helloWorld
 11     return {                                                                     
 12       template: '<div></div>',                                                   
 13       restrict: 'E',                                                             
 14       link: function postLink(scope, element, attrs) {                           
 15         element.text('this is the helloWorld directive');                        
 16       }                                                                          
 17     };  
Defines a template (I usually use templateUrl instead to map to an html file)
Restricts the usage to an element (this can be C for class, A for attribute, or any combination thereof)
Creates a link function. Let's discuss this further.

angularjs directive

Directives have functions that run at different times in their life cycle. This is one of the more complex ideas in angularjs and is especially important to transclusion (accessing scopes outside of your own)

Compile - This is a function that runs at compile time. We have the raw template at this step, it only runs once. 
Controller - Runs once per instance. Contains scope and simple element with no data bindings. Scope is not yet set up
PreLink - Rarely used.
PostLink - Place where DOM manipulation should happen, event listeners bound. Last function to execute. Scope is now set up.
Made with Slides.com