Getting Started in Angular
What I do


So You're Going To Teach Us Angular In 1Hr?!?!?!?!
No
Goals Of This Presentation
- Give an understanding of the framework
- Give some examples that can be referenced later
- Help someone decide which paths to take when starting an Angular project.
-
Why
-
How
-
Next
Why use Angular?




What used to matter



What matters Now
Flourishing Community
Source Redmonk | X: # Github Y: # Stackoverflow
Typical MVC
Typically an MVC framework will refer to the controller to update the view.
This works great for simple or extremely secure applications.
But not everything needs to be done on the server!

JQuery
Now we need a way to manage our view models.
One way to handle this is with JQuery. JQuery provides an easy interface for manipulating the DOM
JQuery tends to get really complicated.
Server
Browser
JQuery
DOM
Angular
Framework to help manage data bindings.
Most important for real-time frequently updating applications.
Compiles directly into the DOM. So no complex selectors.
Server
Browser
Angular
$rootScope
DOM
So What "IS" Angular?
- Javascript
- MVC platform for the client
- Allows for efficient 2-way Data Binding
- Supported by Google*
Grails/Angular Visualized

Pick Your Poison
- 1.2.x
- Use if you are using IE8-IE9
- 1.3.x
- Cutting edge stable
- 1.4.x
- Release Candidate
- Improves Performance
- 2.x
- ECMA 6 Compatible
- Not finished
- Super-Fast/ Simple
What Is A
"Single Page Application"?

Easiest Metric: How many flashes do you see?
Routes
Instead of pages Angular uses routes.
Think of it as you render one page, then let Angular control navigation.
There are 3rd party routings like the Angular-UI project. However, we will talk more in the future of Angular section.

Single Page Apps Feel Less Like Web Apps
Directives
Routes will typically paint Directives. These are also known as "Web Components"
This allows for reusability and encapsulation.
Makes testing a whole lot easier.
More later
A little outdated but...

Easy Transition To Native Application
Built In Interfaces
- iOS: UIWebView
- Android: WebView
- Third Party : Cordova/PhoneGap
Testing
- Unit Testing: Jasmine
- End2End: Cucumber
How To Become an Angular Super Hero!

Be Patient!

OMG there is an Angular 2!
But it is 5x faster
Study The Code

https://github.com/angular/angular
https://github.com/angular/angular.js
Plnkr Helps You To Learn

Angular Application Structure
Project Structure
-
Maven Style
-
"Component" Style
Maven Style
Separate folders for each type of code
Tests are separate from actual code
Tries to produce "components" but just seems to add complexity.
Works well for "legacy" projects
"Component" Style
The idea is to break everything into small "units". To do this we include everything in one directory.
Fits with the idea of "Shadow DOM" and Web Components.
This will be fairly common in next-gen browsers.

The Core Of Angular: Modules
// Create a new Module
var app1 = angular.module('plunker', []);
// Access an existing module
var app2 = angular.module('plunker');
// Create another module with a dependency
var app3 = angular.module('other', [
'restangular'
]);
$RootScope
- RootScope is the "Scope God"
- It manages and updates all of the scopes in your project.
- It can be used to broadcast module wide messages.
- Used to create scope instances in testing.
Next: The Controller
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
Templating (HTML2JS)
.add-note.question
.row
h3.col-xs-offset-1 Add A Question
.row.well(ng-if="questionCtrl.errorMessages.length > 0")
.validation-error(ng-repeat="errorMessage in questionCtrl.errorMessages")
p {{errorMessage}}
.row
.input-group
label(for="questionText") Question Text
input#questionText(name="questionText", type="text", ng-model="questionCtrl.newQuestion.text")
.input-group
label(for="questionDesc") Question Description
text-angular(ng-model="questionCtrl.newQuestion.desc")
.row.pad-top
.col-sm-offset-2
button(class="btn btn-primary",ng-click="questionCtrl.add()") Add Note
angular.module("main/partials/question/addQuestion.jade", []).run(["$templateCache", function($templateCache) {
$templateCache.put("main/partials/question/addQuestion.jade",
"<div class=\"add-note question\">\n" +
" <div class=\"row\">\n" +
" <h3 class=\"col-xs-offset-1\">Add A Question</h3>\n" +
" </div>\n" +
" <div ng-if=\"questionCtrl.errorMessages.length > 0\" class=\"row well\">\n" +
" <div ng-repeat=\"errorMessage in questionCtrl.errorMessages\" class=\"validation-error\">\n" +
" <p>{{errorMessage}}</p>\n" +
" </div>\n" +
" </div>\n" +
" <div class=\"row\">\n" +
" <div class=\"input-group\">\n" +
" <label for=\"questionText\">Question Text</label>\n" +
" <input id=\"questionText\" name=\"questionText\" type=\"text\" ng-model=\"questionCtrl.newQuestion.text\"/>\n" +
" </div>\n" +
" <div class=\"input-group\"> \n" +
" <label for=\"questionDesc\">Question Description</label>\n" +
" <text-angular ng-model=\"questionCtrl.newQuestion.desc\"></text-angular>\n" +
" </div>\n" +
" </div>\n" +
" <div class=\"row pad-top\"> \n" +
" <div class=\"col-sm-offset-2\">\n" +
" <button ng-click=\"questionCtrl.add()\" class=\"btn btn-primary\">Add Note</button>\n" +
" </div>\n" +
" </div>\n" +
"</div>");
}]);
Dependency Injection
Dependency Injection Requires Annotaion
Dependency Injection
// Controller
function TestController ($scope, $http){...}
// Without annotation (cannot be minified)
app.controller('testCtrl', TestController)
// Using $inject
TestController.$inject = ['$scope', '$http'];
// Without using $inject
app.controller('testCtrl', ['$scope', '$http', function($scope, $http) {...}]);
Services
-
A Singleton within the application
- Shared between multiple controllers etc
- Typically where you will interface with server services
Common Services
-
Most Angular services begin with $
- $http, $compile, $rootScope
- Third party services do not
- restangular, angular-ui
Directives
-
Basically "Custom Tags"
-
Allow one to easily bind Angular to HTML
-
Probably one of the hardest concepts to understand
-
Prepend to avoid collisions
-
Gone in 2.0
List Directive
function ListDirective($templateCache) {
return {
restrict: "E",
templateUrl: 'main/partials/question/listQuestion.jade',
controller: 'questionController',
//Example of how you could use controller as per https://github.com/angular/angular.js/issues/7635
//I left this out because it would add 3 lines and remove 1 ;-)
// controllerAs: 'questionCtrl'
}
};
app.directive('jgNoteList', ListDirective);
Feature: Simple Feature
As a user
I want to add a question
Scenario: Adding a question
Given I am on the main page
When I click the add button
And I fill out question information
Then I should see the new question
CucumberJS
/*jslint node: true */
"use strict";
var Browser = require('zombie'),
assert = require("assert"),
Chance = require('chance');
var WorldConstructor = function WorldConstructor(callback) {
this.browser = new Browser();
this.chance = new Chance();
var addForm = "#add-form";
var _this = this;
function addQuestionFormLoaded(window) {
return window.document.querySelector(addForm);
}
var world = {
visit : function(url, callback) {
this.browser.visit(url);
this.browser.wait(function() {
console.log("Waiting to callback");
callback();
});
}.bind(this),
clickLink : function(selector, callback) {
this.browser.clickLink(selector, function() {
var promise = _this.browser.wait(addQuestionFormLoaded, null)
console.log("Test 123");
promise.then(function() {
console.log("Test");
callback();
})
});
}.bind(this),
fillQuestionForm: function(callback){
var title = this.chance.string({length: 20});
assert.ok(this.browser.query(addForm), "It should have the add form");
this.browser.fill('textArea[id^="taHtmlElement"]', "Test Desc").fill("#questionText", this.title);
this.browser.pressButton("Add Note", function() {
callback();
});
}.bind(this),
checkResults: function(callback){
var x = this.browser.html(".question-text");
var hasText = (x.indexOf(this.title) > -1)
assert.equal(hasText, true, "The title should show up somewhere in the questions text.");
callback();
}.bind(this)
}
callback(world); // tell Cucumber we're finished and to use our world
// object instead of 'this'
};
exports.World = WorldConstructor;
var noteStepDefinitionWrapper = function() {
this.World = require("../support/world.js").World; // overwrite default
// World constructor
this.Given(/^I am on the main page$/, function(callback) {
console.log("step 1");
this.visit('http://localhost:8080/grails-angular', callback);
});
this.When(/^I click the add button$/, function(callback) {
console.log("step 2");
this.clickLink('#add-question', callback);
});
this.When(/^I fill out question information$/, function(callback) {
console.log("step 3");
this.fillQuestionForm(callback);
});
this.Then(/^I should see the new question$/, function(callback) {
console.log("step 4");
this.checkResults(callback)
});
};
module.exports = noteStepDefinitionWrapper;
describe("Testing the question", function() {
var $controller,
$location,
$rootScope,
$scope,
controller,
questionService;
var item = {
put:function(){},
remove:function(){}
}, question;
beforeEach(module('jg.ngGrails'));
var wire = function( _$rootScope_, _$location_, _$controller_, _questionService_, $q){
$controller = _$controller_;
$location = _$location_;
spyOn($location, 'path');
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
questionService = _questionService_;
spyOn(item, 'put');
spyOn(item, 'remove');
var defer = $q.defer();
spyOn(questionService, 'add').and.returnValue(defer.promise);
defer.resolve();
questionService.questions = [item,item];
controller = $controller('questionController', {
$scope: $scope
});
}
beforeEach(inject(wire));
it("Make sure vote up works", function() {
expect(item.put).not.toHaveBeenCalled();
controller.voteUp(1);
expect(item.put).toHaveBeenCalled();
});
it("Make sure vote down works", function() {
expect(item.put).not.toHaveBeenCalled();
controller.voteDown(1);
expect(item.put).toHaveBeenCalled();
});
it("Make sure remove works", function() {
expect(item.remove).not.toHaveBeenCalled();
controller.delete(1);
expect(item.remove).toHaveBeenCalled();
});
it("Make sure the edit is working as expected", function(){
expect(item.put).not.toHaveBeenCalled();
expect($location.path).not.toHaveBeenCalled();
controller.edit(1);
expect(item.put).toHaveBeenCalled();
expect($location.path).toHaveBeenCalledWith('');
})
});
Jasmine test
ES6 And The Future Of Angular

What is ES6
- A Standard Javascript
- Slowly being integrated into modern browsers.
- Allows "scoped" variables
- Introduces native module concept
- More of a "classy" language
Going
- Controllers
- Directives
- Scope
- Module
- JQLite
Coming
- More Generic Syntax
- ShadowDOM
- DI Query
- Benchpress
- WTF Instrumentation (Web Tracing Framework)

http://bit.ly/1tLYgXM

http://bit.ly/1tLYgXM
So How Can We Try This Today?!?!
Should I Develop For 1.X or 2.X?
-
Why
-
How
-
Next
Thanks
Questions?
Scope Lifecycle
-
ngRoute events
-
$routeChangeStart
-
$routeUpdate
-
$routeChangeSuccess
-
-
ngView events
-
$viewContentLoaded
-
- controller initialization
-
link/compile
- pre-link
- post-link
Validation
@Validateable
class Question {
Integer key
String text
String desc
Integer voteCount
List<String> errorMessages
static constraints = {
key unique: true
text blank: false
desc blank: false
errorMessages nullable: true
}
}
def create(obj){
obj.key = key++
obj.voteCount = 0
//Validation needs to happen after adding the key
obj.validate()
if(!obj.hasErrors()){
notes.put(obj.key.toString(), obj)
}
else{
obj.errorMessages = obj?.errors?.allErrors?.collect{messageSource.getMessage(it,null)}
}
obj
}
Getting Started Angular
By Jackie Gleason
Getting Started Angular
- 908