54
Front End Workshops XII. AngularJS - Part 3 Juan Luís Marí [email protected] Pablo Balduz [email protected] Héctor Canto [email protected]

Workshop 14: AngularJS Parte III

Embed Size (px)

Citation preview

Page 1: Workshop 14: AngularJS Parte III

Front End Workshops

XII. AngularJS - Part 3

Juan Luís Marí[email protected]

Pablo [email protected]

Héctor [email protected]

Page 2: Workshop 14: AngularJS Parte III

Overview

“AngularJS is turn-based”

Page 3: Workshop 14: AngularJS Parte III

In previous chapters...

● AngularJS intro● Modules and dependency injection● Filters, Controllers and Services● Routes● Digest cycle ● Data binding● Directives ...

Page 4: Workshop 14: AngularJS Parte III

Overview

● Scope functions

● Patterns

● Testing

● Directives functions (continuation of custom directives)

○ compile, controller and link functions

● Build-in directives

Page 5: Workshop 14: AngularJS Parte III

Scope functions

— Watch, apply & Digest—

Page 6: Workshop 14: AngularJS Parte III

Scope functions

Watch, apply and digest are $scope functions used to monitor and process

binded data in terms of the digest cycle.

There are used very scarcely but it is good to know in order to understand bad

updates, performance issues and digest loop errors.

http://tutorials.jenkov.com/angularjs/watch-digest-apply.html

Page 7: Workshop 14: AngularJS Parte III

Scope functions - watch

Data binding creates ‘watchers’ internally. Every digest cycle will check

watched variables for changes and react accordingly.

Variables are watched implicitly when we use double curly brackets:

{{myVar}}

and explicitly:

$scope.watch(‘myVar’, function(){...});

As 1st parameter we can call a function, also.

The 2nd function is called a ‘listener’.

http://tutorials.jenkov.com/angularjs/watch-digest-apply.html

Page 8: Workshop 14: AngularJS Parte III

Scope functions - apply

Through the digest cycle, watched variables will be monitored.

But sometimes we don’t have a variable to watch and we are waiting for an

async event.

Some directives and services already use apply for us internally.

● ng-click

● $timeout

● $http

and explicitly:

$scope.$apply(function() {

// your code here

});

The 2nd function is a ‘listener’. The ‘value argument’ can be a function too.

http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

Page 9: Workshop 14: AngularJS Parte III

Scope functions - apply

function Ctrl($scope) { $scope.message = "Waiting 2000ms for update"; setTimeout(function () { $scope.$apply(function () { $scope.message = "Timeout called!"; }); }, 2000);}Note: Instead, you could use $timeout.

Page 10: Workshop 14: AngularJS Parte III

Scope functions - apply

When to use apply:● Very rarely ( or better NEVER ). Two well-know cases:

○ dealing with sockets○ wrapping non-Angular code○ when you know a variable is initialized outside the digest cycle

How to use apply:● with a function preferably:

$scope.apply( function() {});● Tip: Use $timeout without delay (but it will be defered).

http://stackoverflow.com/questions/23070822/angular-scope-apply-vs-timeout-as-a-safe-apply

Page 11: Workshop 14: AngularJS Parte III

Scope functions - digest

$scope.digest triggers a digest cycle, but we should never call it directly.

● digest is triggered internally

● after the execution of $scope.apply, at the end of its execution

We use apply (and therefore digest) when we don’t have a variable to watch

and we are waiting for an async event.

http://jimhoskins.com/2012/12/17/angularjs-and-apply.html

Page 12: Workshop 14: AngularJS Parte III

Common Patterns

— non trivial only—

Page 13: Workshop 14: AngularJS Parte III

Known patterns

Angular is already build as a collection of patterns

● Services are singletons.

● Factories are factories.

● Data-binding and $scope.watch implements a watcher pattern.

● Controller and Template.

● Dependency injections: everywhere.

http://blog.mgechev.com/2014/05/08/angularjs-in-patterns-part-1-overview-of-angularjs/

Page 14: Workshop 14: AngularJS Parte III

Observer (publish-subscribe)

Observers are similar to watchers, but instead of permanently watch a fuction they subscribe to events. To subscribe:

$scope.on(‘event-name’, function handler() {

//your code here

}

And to launch the event:function ExampleCtrl($scope) {

$scope.$emit('event-name', { foo: 'bar' }); //upwards on the scope hierarchy

$scope.$broadcast('event-name', { foo: 'bar' }); //downwards to all child

$rootScope.$broadcast('event-name', { foo: 'bar' }); //to everybody

}

http://blog.mgechev.com/2014/07/05/angularjs-in-patterns-part-3/

Page 15: Workshop 14: AngularJS Parte III

Event listener (View observer)

Multiple build-in directives are event listeners:

● ng-click, ng-dbl-click

● ng-mouse…

● ng-keydown, ng-keyup

● ng-change

You can add your own event listeners. Let see an example:

http://tutorials.jenkov.com/angularjs/events.html

Page 16: Workshop 14: AngularJS Parte III

Event listener exampleangular.module('myApp', [])

.directive('myDirective', function myDirective () {

return {

restrict: 'AE',

link: myDirectiveLink

} });

function myDirectiveLink (scope, element, attrs) {

var domElement = element[0]; // Get the native JS element

domElement.addEventListener("dragstart", myEventListener, false); // You can use ngDraggable but it uses jQuery

}

function myEventListener () {

// Handle event here

}

Page 17: Workshop 14: AngularJS Parte III

Other patterns

Proxy

● a service using $http or $resource (Restful http) internally.

Data Mapper

● a service returning Model instances calling $http or $resource.

Page 18: Workshop 14: AngularJS Parte III

Testing

— AngularJS embraces testing—

Page 19: Workshop 14: AngularJS Parte III

AngularJS takes well care of testing:

● Well supported by Karma (test runner)

● Can use Mocha, Sinon, Chai ...

● Protractor and ngMock specially build for testing Angular.

● KEY: inject dependencies and mock key elements

Testing in AngularJS

https://www.smashingmagazine.com/2014/10/introduction-to-unit-testing-in-angularjs/https://quickleft.com/blog/angularjs-unit-testing-for-real-though/

Page 20: Workshop 14: AngularJS Parte III

E2E framework based on Jasmine and Selenium’s WebDriver

Ability to simulate user interaction

Simulation in selenium and most browsers

Usually, we will run tests in Selenium (without UI) and all target browsers.

We can automate this with Gulp

Protractor

Page 21: Workshop 14: AngularJS Parte III

Provides several services to mock Angular behaviour:

● $timeout, ● $controller : inject controllers● $httpBackend: patch or mock $http calls● module() and inject():

○ resolve dependencies on tests○ patch methods○ mock scope

ngMock

http://www.bradoncode.com/blog/2015/05/27/ngmock-fundamentals-angularjs-testing-inject/

Page 22: Workshop 14: AngularJS Parte III

Unit Testing

● Testing individual, small units of code → Need of isolated code○ Note: Bad isolated code may force the app to create related pieces as server’s

requests or new DOM elements → Affect other tests and produce errors

● Solutions: Dependency Injection and Mocking Services○ DOM elements abstracted → never directly modified

○ XHR requests and petitions → simulated (by dependency injection of $httpBackend)

■ All we really need is to verify whether a certain request has been sent or not, or

alternatively just let the application make requests, respond with pre-trained

responses and assert that the end result is what we expect it to be.

Page 23: Workshop 14: AngularJS Parte III

Dependency Injection ● AngularJS built-in. Allows to pass in a

component's dependencies and stub or mock

them as you wish.

● Does not modify any global variable, so it does

not affect other tests

angular.module('app', []).controller(ExampleCtrl, function ExampleCtrl($scope) {

...});

describe('ExampleCtrl tests', function() { beforeEach(module('app'));

var $controller;

beforeEach(inject(function(_$controller_){ // The injector unwraps the underscores (_) from around the parameter names when matching $controller = _$controller_; }));

describe('block to be tested', function() { it('test1', function() { // create a scope object for us to use. var $scope = {}; // now run that scope through the controller function, injecting any services or other injectables we need. // **NOTE**: this is the only time the controller function will be run, so anything that occurs inside of that will already be done before the first spec. var controller = $controller('ExampleController', { $scope: $scope }); }; }); });

Page 24: Workshop 14: AngularJS Parte III

$httpBackend● angular-mocks module allows to inject and mock the AngularJS services, so they can be extended

and used synchronously → $httpBackend is one of them

● Service in module ngMock

● Fake HTTP backend implementation suitable for unit testing applications that use the $http service.

● Allows not using external dependencies, so the requests to URLs are mocked.

● With dependency injection, it is easy to inject $httpBackend mock and use it to verify the requests

and respond with some testing data without sending a request to a real server.

● $httpBackend.flush() allows to flush pending requests, so the test will run synchronous but

preserving the async of the backend API

Page 25: Workshop 14: AngularJS Parte III

Custom directives - Continuation

— DOM Manipulation—

Page 26: Workshop 14: AngularJS Parte III

There are 3 types of functions, by order of execution:○ compile, controller and link

● Compile happens once, before the template is compiled.● The rest of functions is run once for each time the directive is used

■ For example in a ng-repeat of 4 elements, 4 loops○ Controller initialize the scope.○ Link happens when the linking is being made, by default after.○ We can divide link into two, pre-link and post-link

■ Pre-link happens before the element is linked to the scope■ Post-link happens just after, when the element affected is on the DOM.

● This is the most usual and potentially safest

Custom directives: functions

http://www.undefinednull.com/2014/07/07/practical-guide-to-prelink-postlink-and-controller-methods-of-angular-directives/

Page 27: Workshop 14: AngularJS Parte III

compile: function CompilingFunction($templateElement, $templateAttributes) {

…}

link: function LinkingFunction($scope, $element, $attributes) { ... }

…}

link: {

pre: function PreLinkingFunction($scope, $element, $attributes) { ... },

post: function PostLinkingFunction($scope, $element, $attributes) { ... },

}

Custom directives: functions

http://plnkr.co/edit/qrDMJBlnwdNlfBqEEXL2?p=previewhttps://github.com/angular/angular.js/wiki/Understanding-Directives

Page 28: Workshop 14: AngularJS Parte III

Custom directives: link, prelink, postlink

● There are 4 arguments available for these functions (in this order)

○ scope, elements, attributes and controllers

● You can access the DOM, you have the element.

● By default use link directly, which is equivalent to post-link alone.

● Remember, if possible provide values as soon as you can.

○ Don’t wait to post-link, do it in the controller or in compile

● [post-]link is the View part where you have everything in place and you do

the last adjustments and decisions regarding the DOM.

Page 29: Workshop 14: AngularJS Parte III

Custom directives: link, prelink, postlinkvar app = angular.module('app', []);app.directive('dad', function () { return { restrict: 'EA', template: '<div class="dad">{{greeting}}{{name}}'+ '<son></son>'+ '</div>', link: { pre: function(scope,elem,attr){ scope.name = 'Paul'; scope.greeting = 'Hey, I am '; } } };})

app.directive('son', function () { return { restrict: 'EA', template: '<div class="son">{{sonSays}}</div>', link: function(scope,elem,attr){ scope.sonSays = 'Hey, I am David, and my dad is '+ scope.name; } };});

<div ng-app="app"> <dad></dad></div>

Hey, I am PaulHey, I am David, and my dad is Paul

Page 30: Workshop 14: AngularJS Parte III

Custom directives: post-link,

● It is safe to manipulate the DOM in post-link as the element is already in

the DOM.

● It is possible to access the scope

● All child directives are linked so it’s safe to access them

○ their scope and the elements they affect.

● It is safe to attach events handlers to elements.

Page 31: Workshop 14: AngularJS Parte III

Custom directives: pre-link,

● Use of pre-link is scarce,

○ A child needs data from its parent

● Safe to attach an event to the DOM element

○ Not safe to access DOM elements from child directives

● The scope is not linked yet.

Page 32: Workshop 14: AngularJS Parte III

Custom directives: compile

● In this phase AngularJS manipulates the DOM of the HTML template

● Each directive has a chance to do some processing for all and each DOM

nodes it appears in.

● The scope is not attached yet.

● The template is still bare, without binding nor substitutions.

Page 33: Workshop 14: AngularJS Parte III

Built-in directives

Page 34: Workshop 14: AngularJS Parte III

Built-in directives

● ng-model● ng-src● ng-class● ng-style● ng-click● ng-if● ng-show● ng-include● ng-repeat

Page 35: Workshop 14: AngularJS Parte III

ng-model● Binds inputs to scope properties● If property does not exist, it is created and added to the scope● Watches model by reference Important!● Allows animation and validation

<script>

angular.module("module", [])

.controller("controller", ['$scope', function($scope) {

$scope.value = "Luis";

}]);

</script>

<div ng-controller="controller">

Name: <input ng-model="value"/>

</div>

Page 36: Workshop 14: AngularJS Parte III

ng-src● src leads to problems when evaluating expressions● Prevents browser from loading resources before an expression is

evaluated

Buggy way

Correct way

<img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>

<img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />

Page 37: Workshop 14: AngularJS Parte III

ng-class

● Allows to dynamically set CSS classes on an HTML● Ways of operating (syntax):

○ String○ Key-value pair object○ Array of strings (type 1) or objects (type 2)○ Ternary operator

● Usage options: class and attribute

Page 38: Workshop 14: AngularJS Parte III

ng-classString syntax

Array syntax

Key-value pair syntax

<input type="text" ng-model="style">

<div ng-class="style">String syntax</div>

<input type="text" ng-model="styleOne">

<input type="text" ng-model="styleTwo">

<div ng-class="[styleOne, styleTwo]">Array syntax</div>

<input type="checkbox" ng-model="great"> Barça

<input type="checkbox" ng-model="poor"> Madrid

<div ng-class="{ styleOne: great, styleTwo: poor }">

Page 39: Workshop 14: AngularJS Parte III

ng-classTernary operator

Specific numbers

ngClass as a class

ngClass as an attribute (every example shown except the last one)

ng-class="variableToEvaluate ? 'class-if-true' : 'class-if-false'">

<ul><li ng-class="{ 'text-success': $first }" ng-repeat="item in items">{{ item.name }}</li></ul>

<div class="item ng-class:type;">Stuff</div>

<div class="item ng-class:[styleOne, styleTwo];">Stuff</div>

<div class="item ng-class:{ 'text-error': wrong };">Stuff</div>

Page 40: Workshop 14: AngularJS Parte III

ng-style

● Allows setting style on an HTML conditionally● Interpolates javascript object into style attribute, not css class

Following directive will be translated to style=”color:red”

Following directive will be translated to class=”color”

For interpolation, instead of doing this:

do this:

ng-style="{color: 'red'}"

ng-class="{color: colorClass}"

style="width: {{progress}}"

ng-style="{width: progress}"

Page 41: Workshop 14: AngularJS Parte III

ng-click

● Specify custom behavior when an element is clicked

<button ng-click="count = 0">

Reset

</button>

<button ng-click="reset()">

Reset

</button>

Page 42: Workshop 14: AngularJS Parte III

ng-show

● Shows / hides HTML element based on an expression● Adds / removes ng-hide class● Animations

Element visible when $scope.value is truthy

Element hidden when $scope.value is falsy

<div ng-show="value"></div>

<div ng-show="value" class="ng-hide"></div>

Page 43: Workshop 14: AngularJS Parte III

ng-if

● Removes / recreates part of the DOM● scope is removed and recreated● ng-model with ng-if issues● Animations

Similar usage to ngShow / ngHide<div ng-if="value"></div>

Page 44: Workshop 14: AngularJS Parte III

ngAnimate

● Include angular-animate.js and load module● Directives support

○ ngRepeat○ ngShow○ ngHide○ ngIf○ ...

● CSS / JS based animations

Page 45: Workshop 14: AngularJS Parte III

ngAnimate

CSS based animations

● No need of JavaScript code at all● CSS class referenced between HTML and CSS● 2 css classes added depending on the animation event

<div ng-if="bool" class="fade">

Fade me in out

</div>

<button ng-click="bool=true">Fade In!</button>

<button ng-click="bool=false">Fade Out!</button>

.fade.ng-enter {

transition:0.5s linear all;

opacity:0;

}

.fade.ng-enter.ng-enter-active {

opacity:1;

}

Page 46: Workshop 14: AngularJS Parte III

ngAnimate

CSS based animations

● Staggering animations● ng-Animate css class● Custom keyframe animations

.zipper.ng-animate {

transition:0.5s linear all;

}

.zipper.ng-enter {

opacity:0;

}

.zipper.ng-enter.ng-enter-active {

opacity:1;

}

.zipper.ng-leave {

opacity:1;

}

.zipper.ng-leave.ng-leave-active {

opacity:0;

}

Page 47: Workshop 14: AngularJS Parte III

ngAnimate

JS based animations

Register JavaScript animation on the module

<div ng-repeat="item in items" class="slide">

{{ item }}

</div>

myModule.animation('.slide', [function() {

return {

enter: function(element, doneFn) {

// Do some cool animation and call doneFn

},

move: function(element, doneFn) {},

leave: function(element, doneFn) {}

}

}]);

Page 48: Workshop 14: AngularJS Parte III

ngAnimate

ngAnimate documentation: https://docs.angularjs.org/api/ngAnimate

Page 49: Workshop 14: AngularJS Parte III

ng-include● Add HTML code from external file to the current one

○ Fetches, compiles and includes an external HTML fragment● Create new scope for the included template● The included HTML files can also contain AngularJS code

<div ng-include="'myFile.html'"></div>

Page 50: Workshop 14: AngularJS Parte III

ng-include

● Cross-domain:○ By default, the ng-include directive does not allow you to include

files from other domains.○ To include files from another domain, you can add a whitelist of

legal files and/or domains in the config function of your application:

var app = angular.module('myApp', [])

app.config(function($sceDelegateProvider) {

$sceDelegateProvider.resourceUrlWhitelist([

'http://www.refsnesdata.no/**'

]);

});

Page 51: Workshop 14: AngularJS Parte III

ng-repeat

● Instantiates a template once per item from a collection<div ng-repeat="item in collection"> ... </div>

● Each template instance gets its own scope

● It is also possible to get ngRepeat to iterate over the properties of an object○ Note: The built-in filters orderBy and filter do not work with objects

<div ng-repeat="(key, value) in myObj"> ... </div>

Page 53: Workshop 14: AngularJS Parte III

ng-repeat

● Continuously watching if changes in the collection happens○ Automatically changing DOM (when adding, removing or reordering items)

○ “Keep track” items with their DOM elements → Avoids re-render them if not needed

● Default tracking function (changeable with track-by)○ Tracks by identity of item

○ Does not allows duplicated items

● ng-repeat in more than one parent element of the HTML○ ng-repeat-start

○ ng-repeat-endrange of the repeater

Page 54: Workshop 14: AngularJS Parte III