AngularJS

Introduction & Overview
https://github.com/derek-meulmeester/can-wic-2016
patricia kaminska

derek meulmeester

on the menu
past
current
future
why angular exists
why angular exists
2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)




why angular exists
2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)




Primarily Server-Side Scripting Languages



why angular exists
2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)




Primarily Server-Side Scripting Languages



- Browsers were inconsistent
- Browsers were slow
why angular exists
2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)
- Browsers were slow
2006:

- Browsers were inconsistent
why angular exists
2006:

2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)
- Browsers were slow
Browsers were inconsistent


why angular exists
2006:

2008:

2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)

- Browsers were slow
Browsers were inconsistent
why angular exists
2008:

2006:
2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)


Browsers were slow
Browsers were inconsistent
why angular exists
2008:

Angular Initial Release
Backbone.js
Knockout etc.

2009:
2006:
2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)


Browsers were inconsistent
Browsers were slow
why angular exists
Angular Initial Release
Backbone.js
Knockout etc.

2009:

Angular 1.0 - 2012:
2006:
2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)


2008:

why angular exists


Angular 1.0 - 2012:
- Client-Side HTML Templating
- Extending HTML (Directives)
- Two-Way Data Binding
2006:
2000 - 2004: There's a growing dependence on the web (Social Networks, Google Search, etc.)


2008:
2009:
Angular Initial Release
Backbone.js
Knockout etc.

ng-philosophy
Declarative vs Imperative
<html>
<body>
...
while(canCwic){
...
}
AngularJs
View
+
Ctrl
+
Services
TDD
DOM
MANIPULATION
Boring
code
learning

angular - An Overview
Module
View
$scope
Directive
Controller
Service
angular
Objective

module
angular.module('tekFlix', []);
<div ng-app="tekFlix">
<input type="text" ng-model="msg"/>
<br><br>
{{ msg }}
</div>
JavaScript
HTML
Result

directive
angular.module('tekFlix', [])
.directive('tekMovie', function() {
return {
restrict: 'E',
scope: {
src: '@src'
},
template: '<img class="tek-movie" ng-src="{{src}}"/>'
};
});
<div ng-app="tekFlix">
<tek-movie src="http://cdn1.nflximg.net/webp/2355/8772355.webp"></tek-movie>
<tek-movie src="http://cdn0.nflximg.net/webp/0932/8140932.webp"></tek-movie>
</div>
JavaScript
HTML
directive
Result

directive
angular.module('tekFlix', [])
.directive('tekMovieSpinner', function() {
return {
restrict: 'E',
template: '<h2>{{ data.genre }}</h2>' +
'<ul class="tek-spinner">' +
'<li ng-repeat="movie in data.movies">' +
'<tek-movie src="{{movie.img_url}}"></tek-movie>' +
'</li>' +
'</ul>',
link: function($scope) {
$scope.data = SPINNER_DATA;
}
};
});
var SPINNER_DATA = {
genre: 'Comedies',
movies: [{
img_url: 'http://cdn0.nflximg.net/webp/6174/11626174.webp'
}, {
img_url: 'http://cdn0.nflximg.net/webp/4714/22964714.webp'
}]
}
<div ng-app="tekFlix">
<tek-movie-spinner></tek-movie-spinner>
</div>
directive

Result
directive - test
describe('tekMovieSpinner Test', function() {
var $compile;
var $scope;
beforeEach(module('tekFlix'));
beforeEach(inject(function(_$compile_, _$rootScope_){
$compile = _$compile_;
$scope = _$rootScope_.$new({});
}));
it('tekMovieSpinner - replaces the movie url as expected',function(){
$scope.movies = [{
img_url:"http://cdn1.nflximg.net/webp/6325/8956325.webp",
title:"Star Trek the Next Generation"}];
var beforeElement =
angular.element('<tek-movie-spinner movies="movies"></tek-movie-spinner>');
var compiledElement = $compile(beforeElement)($scope);
$scope.$digest();
var img = compiledElement.find("li").find("img");
expect(img.attr("src")).toEqual("http://cdn1.nflximg.net/webp/6325/8956325.webp");
});
});
directive
<div ng-app="tekFlix">
<tek-spinner-container></tek-spinner-container>
</div>
angular.module('tekFlix', [])
.directive('tekSpinnerContainer', function() {
return {
restrict: 'E',
template: '<div ng-repeat="spinner in spinners" class="spinner-container">' +
'<tek-movie-spinner data="spinner"></tek-movie-spinner>' +
'</div>',
link: function($scope) {
$scope.spinners = SPINNER_DATA;
}
}
});
var SPINNER_DATA = [{
genre: 'Comedies',
movies: [{
img_url: 'http://cdn0.nflximg.net/webp/6174/11626174.webp'
}, {
img_url: 'http://cdn0.nflximg.net/webp/4714/22964714.webp'
}]
}, {
genre: 'Sci-Fi',
movies: ...
}]
directive
Result

service
angular.module('tekFlix', [])
.service('movies', function($http) {
this.getSpinnerData = function() {
return $http.get('http://localhost:3000/data');
};
})
.directive('tekSpinnerContainer', function(movies) {
return {
...
link: function($scope) {
//$scope.spinners = SPINNER_DATA;
movies.getSpinnerData().then(function(result) {
$scope.spinners = result.data;
});
}
};
});
service
angular.module('tekFlix', [])
.service('movies', function($http) {
this.getSpinnerData = function() {
return $http.get('http://localhost:3000/data');
};
})
.directive('tekSpinnerContainer', function(movies) {
return {
...
link: function($scope) {
//$scope.spinners = SPINNER_DATA;
movies.getSpinnerData().then(function(result) {
$scope.spinners = result.data;
});
}
};
});
Dependency Injection!
service
angular.module('tekFlix', [])
.service('movies', function($http) {
this.getSpinnerData = function() {
return $http.get('http://localhost:3000/data');
};
})
.directive('tekSpinnerContainer', function(movies) {
return {
...
link: function($scope) {
//$scope.spinners = SPINNER_DATA;
movies.getSpinnerData().then(function(result) {
$scope.spinners = result.data;
});
}
};
});
Dependency Injection!
service
Result

service-Test
describe('movies Service', function() {
var $httpBackend;
var movies;
beforeEach(module('tekFlix');
beforeEach(inject(function($injector) {
movies = $injector.get('movies');
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('GET', 'http://localhost:3000/data')
.respond([{
genre: 'Comedy',
movies:[{ title: 'Archer', img_url: 'http://blah.jpg'}]
}]);
}));
it('Will be able to fetch spinner data', function(){
var result;
movies.getSpinnerData().then(function(response) {
result = response.data;
});
$httpBackend.flush();
expect(result).toBeDefined();
expect(result.length).toBe(1);
expect(result[0].genre).toBe('Comedy');
});
});
FILTER
<div ng-app="tekFlix">
<nav>
<input ng-model="search" class="search-box" placeholder="Titles, people, genres"/>
<h1>tekFlix</h1>
</nav>
<tek-spinner-container search="search"></tek-spinner-container>
</div>
<div class="spinner-container" ng-repeat="spinner in spinners | filter:search">
<h2>{{ spinner.genre }}</h2>
<tek-movie-spinner movies="spinner.movies" search="search"></tek-movie-spinner>
</div>
<ul class="tek-spinner">
<li ng-repeat="movie in movies | filter:search">
<img class="ms-movie" ng-src="{{ movie.img_url }}"/>
</li>
</ul>
<body></body>
<tek-spinner-container></tek-spinner-container>
<tek-movie-spinner></tek-movie-spinner>
FILTER
Result

FILTER
Result

angular
Result
e2e Testing
describe('Movie Spinner', function() {
it('should filter movies', function() {
// Find the element with ng-model='search' and type 'star trek' into it
element(by.model("search")).sendKeys("star trek");
// Find the search button on the page and click it
element(by.css(".search-btn")).click();
});
});
Protractor - find elements & setup scenario
e2e Testing
describe('Movie Spinner', function() {
it('should filter movies', function() {
// Find the element with ng-model='search' and type 'star trek' into it
element(by.model("search")).sendKeys("star trek");
// Find the search button on the page and click it
element(by.css(".search-btn")).click();
// Verify that there are 2 movies
expect(element.all(by.repeater("movie in movies")).count()).toEqual(2);
});
});
Protractor - assert expectations
e2e Testing
describe('Movie Spinner', function() {
it('should filter movies', function() {
// Find the element with ng-model='search' and type 'star trek' into it
element(by.model("search")).sendKeys("star trek");
// Find the search button on the page and click it
element(by.css(".search-btn")).click();
// Verify that there are 2 movies
expect(element.all(by.repeater("movie in movies")).count()).toEqual(2);
// Enter 'lebowski' into the search box
element(by.model("search")).sendKeys("lebowski");
// Verify that now there is only 1 movie in the spinner
expect(element.all(by.repeater("movie in movies")).count()).toEqual(1);
});
});
Protractor - rinse and repeat
e2e Testing
describe('Movie Spinner', function() {
it('should filter movies', function() {
// Don't care how it's done, just search for 'star trek'
BrowsePage.search("star trek");
// Click search button
BrowsePage.getSearchBtn().click();
// Verify that there are 2 movies
expect(element.all(by.repeater("movie in movies")).count()).toEqual(2);
// Instead, search for 'lebowski'
BrowsePage.search("lebowski");
// Verify that now there is only 1 movie in the spinner
expect(element.all(by.repeater("movie in movies")).count()).toEqual(1);
});
});
Protractor + Page Object =
Angular 2.x
TypeScript || Dart || Js
Ctrl
Scopes
2-way
databinding
... ish
Directive
Component
I'll just fork 1.x myself
your.friend = ng-upgrade
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
2
to upgrade or not to upgrade
Speed
1.x
2.x
questions?
AngularJS-CAN-WIC-2016
By meulmees
AngularJS-CAN-WIC-2016
- 895