workshop 14: angularjs parte iii

Post on 14-Jan-2017

274 Views

Category:

Software

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Front End Workshops

XII. AngularJS - Part 3

Juan Luís Maríjlmari@visual-engin.com

Pablo Balduzpbalduz@visual-engin.com

Héctor Cantohcanto@visual-engin.com

Overview

“AngularJS is turn-based”

In previous chapters...

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

Overview

● Scope functions

● Patterns

● Testing

● Directives functions (continuation of custom directives)

○ compile, controller and link functions

● Build-in directives

Scope functions

— Watch, apply & Digest—

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

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

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

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.

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

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

Common Patterns

— non trivial only—

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/

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/

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

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

}

Other patterns

Proxy

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

Data Mapper

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

Testing

— AngularJS embraces testing—

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/

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

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/

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.

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 }); }; }); });

$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

Custom directives - Continuation

— DOM Manipulation—

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/

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

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.

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

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.

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.

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.

Built-in directives

Built-in directives

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

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>

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" />

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

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 }">

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>

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}"

ng-click

● Specify custom behavior when an element is clicked

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

Reset

</button>

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

Reset

</button>

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>

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>

ngAnimate

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

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

● CSS / JS based animations

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;

}

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;

}

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) {}

}

}]);

ngAnimate

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

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>

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/**'

]);

});

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>

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

top related