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

  • 817