The words you are searching are inside this book. To get more targeted content, please make full-text search by clicking here.
Discover the best professional documents and content resources in AnyFlip Document Base.
Search
Published by , 2015-10-18 11:31:40

angularjs1.4

angularjs1.4

Attribut par scope - @

angular.module('app', [])
.directive('myShadow',myShadowDirective);

function myShadowDirective(){
return {
restrict: 'A',
scope: {
shadowColor: '@'
},
link: function(scope,element,attrs){
attrs.$observe('shadowColor',function(){
element.css('box-shadow','4px 4px 4px '+scope.shadowColor);
})
}
}

}

<section ng-app="app">
<p my-shadow shadow-color="blue">
lorem ipsum...
</p>

</section>

http://codepen.io/Ambient-IT/pen/EjvaoE?editors=101

Attribut par scope (=)

angular.module('app', []) <section ng-app="app" ng-controller="DemoController as ctrl">
.controller('DemoController',DemoController) <p my-shadow shadow-config="ctrl.shadowConfig">
.directive('myShadow',myShadowDirective); lorem ipsum
</p>
function DemoController(){
this.shadowConfig = { </section>
width: 2,
height: 2,
deep: 2,
color: '#d01616'
}

}

function myShadowDirective(){
return {
restrict: 'A',
scope: {
shadowConfig: '='
},
link: function(scope,element,attrs){
attrs.$observe('shadowConfig',function(){
var conf = scope.shadowConfig;
var shadowStr = conf.width+'px '+conf.height+'px '+conf.deep+'px '+conf.color;
element.css('box-shadow',shadowStr);
})
}
}

}

http://codepen.io/Ambient-IT/pen/JdJQVr?editors=101

Attribut par scope (&)

angular.module('app',[])
.directive('demo',demoDirective)
.controller('DemoController',DemoController);

function demoDirective(){
return {
template: '<div><span>Hello </span>'+
'<span ng-click="demoCtrl.worldClicked()">World</span>'+
'</div>',
scope:{},
controller: function(){
var self = this;
this.worldClicked = function(){
self.demoCtrl.clicked('hello !!!');
}
},
controllerAs: 'demoCtrl',
bindToCtrl: {
clicked: '&'
}
}

}

function DemoController(){
this.direciveClicked = function(message){
alert(message);
}

}

<section ng-app="app" ng-controller="DemoController as ctrl">
<div demo clicked="ctrl.directiveClicked"></div>

</section>

http://codepen.io/Ambient-IT/pen/jPLEeB?editors=101

Transclusion

angular.module('app',[])
.directive('demo',demoDirective);

function demoDirective(){
return {
transclude: true,
template: '<article><p>Hello </p>'+
'<p ng-transclude></p>'+
'</article>'
}

}

La transclusion permet d'insérer un peu de HTML à
un endroit précis du template de la directive

<section ng-app="app">
<h1>Demo !!!!</h1>
<demo>
<div>
I will be included in ng-transclude DOM's node
</div>
</demo>

</section>

Intéraction entre directives

Afin de créer des composants complexes, il est
possible d' imposer l'utilisation conjointe de
plusieurs directives.

Il faut pour cela utiliser l'attribut require dans
la definition d'une directive.

angular.module('app',[]) <section ng-app="app">
.directive('demo',demoDirective); <demo ng-model="test.value">

function demoDirective(){ </demo>
return { </section>
restrict: 'E',
require: 'ngModel',
link: function(scope,element,attrs,ngModelCtrl){
//we can manipulate ngModel's value
}
}

}

$observe

$observe fonctionne de la
même manière que $watch.

$observe n'est utilisable que dans une
directive, en effet $observe ne peut être
utilisé qu'avec le DOM.

Il faut toujours privilégier l'utilisation de $observe par rapport à
scope.$watch dans une directive.

Validation de formulaire personnalisée

Depuis la version 1.3, il est possible de créer ses
propres règles de validation .

$validators

$validators est un nouvel attribut de ngModel qui permet
d'ajouter un ou plusieurs validateurs "custom" au modèle.

$validators attend en retour un booléen .

$asyncValidators

Fonctionne de la même maniére que $validators mais pour les
traitements asynchrones.

$asyncValidators attend en retour une promise (s'utilise très
bien avec $http ).

Ces deux techniques sont à utiliser dans une directive.

$pending

L'attribut $pending permet d'afficher un message ou un
spinner en attendant la réponse du validateur asynchrone.

http://codepen.io/Ambient-IT/pen/KpvdJw?editors=101

Angular core

"data-binding under the hood"

Les watchers

A chaque fois que l'on utilise ng-model, ng-repeat et
d'autres, angular met en place des "watchers"

Un "watcher" est une simple fonction qui sera appelée
par le framework pour mettre à jour le DOM

Nous pouvons déclarer nos propres watchers...

// Si l'on souhaite observer un attribut de $scope
$scope.$watch('title', function(newVal, oldVal) {

//faire quelque chose
});

// A utiliser si on souhaite observer autre chose que le scope
$scope.$watch(function() {

return myService.data.title;
}, function(newVal, oldVal) {

//faire quelque chose
});

// Un troisième argument de type booléen permet d'observer un objet en profondeur
$scope.$watch(function(){

return myService.data;
}, function(newVal, oldVal) {

// ici c'est tout l'objet myService.data qui est observé
},true);

Mais egalement surveiller une collection (un array)

function MyController($scope){
var self = this;
self.collection = [{
id: 1,
content: 'foo'
}];

$scope.$watchCollection(function() {
return self.collection;

}, function(newVal, oldVal) {
//faire quelque chose

});
}

angular.module('app',[])
.controller('MyController',MyController);

Depuis la version 1.3 watchGroup

La méthode $watchGroup de $scope permet de mettre en
place plusieurs watchers d'un coup en specifiant un array
d'expressions

//ici $scope.foo et $scope.bar seront observés
$scope.$watchGroup(['foo', 'bar'], function(newVal, oldVal, scope) {

//faire quelque chose
});

$scope.$watchGroup(function() {
return [
myService.data,
'foo'
]

}, function(newVal, oldVal, scope) {
//faire quelque chose

});

$digest et $apply

"Dirty checking"

$scope.$digest() va exécuter tous les watchers liés au scope.

$scope.$apply() va appeler $rootScope.$digest(), ce qui va
exécuter tous les watchers.
$digest permet à angular de maintenir le modèle et la vue
synchronisés.

Le framework lance $digest à chaque fois :
qu'un bouton avec la directive ng-click est cliqué
que la valeur d'un input bindé avec ng-model change
qu'un callback asynchrone est exécuté...

http://ryanclark.me/how-angularjs-implements-dirty-checking/

Modules Complémentaires

Sécurité ngSanitize

Permet de sécuriser les saisies utilisateurs, afin d'éviter toute
injection de code malicieux.

bower install ng-sanitize --save

angular.module('app',[
'ngSanitize'

])

<script src=""></script>

ngSanitize nous permet d'utiliser la directive ng-bind-html.

Le html sera parsé et tout élément dangereux supprimé

Pour outrepasser ngSanitize et injecter du html
complexe, on peut utiliser le service $sce.

De plus le module fournit un filtre linky
Ce filtre permet de transformer toutes les URL d'une String
en lien HTML (balise <a>)

Ce filtre s'utilise avec ng-bind ou ng-bind-html.

Exercice

Intégrer ngSanitize dans notre application, et utiliser le filtre
linky sur la page de details d'un post.

animation ngAnimate

Le module ngAnimate permet d'utiliser directement des
transitions et keyframes CSS3.

bower install ng-animate --save

angular.module('app',[
'ngAnimate'

])

<script src=""></script>

ngRepeat enter and leave

ngView enter and leave
ngInclude enter and leave

ngSwitch enter and leave
ngIf enter and leave

ngShow & ngHide add and remove
form & ngModel add and remove (dirty,
pristine, valid, invalid & all other
validations)

ngMessages add and remove (ng-active &
ng-inactive)
ngMessage

Le framework va tout simplement ajouter des classes CSS à
nos éléments html quand ils entrent dans le DOM, ou juste
avant d'en sortir.

.ng-enter
.ng-enter-active
.ng-leave
.ng-leave-active



Exercice

Modifier le fichier style.css afin d'animer la liste de posts dans
le template list.tpl.html
l'animation fonctionnera conjointement avec le filtre

mobile ngTouch

Le module ngTouch fournit plusieurs directives adaptées aux
écrans tactiles

bower install ng-touch --save

angular.module('app',[
'ngTouch'

])

<script src=""></script>

Homogénéisation de ngClick, le comportement sera le même
sur mobile et desktop (délai de 300ms sur mobile)

Deux directives :
ngSwipeLeft ;
ngSwipeRight ;

Ces deux directives fonctionnent comme ngClick.

bon guide sur angular et le mobile IonicFramework
angular-gesture

Tester une application
AngularJS

Tests unitaires

Karma

Outil simplifiant l'exécution des tests
Permet de tester plusieurs navigateurs simultanément
Supporte plusieurs frameworks de test

Jasmine
Mocha
QUnit
...

Tests Fonctionnels (E2E)

Protractor

Framework de tests fonctionnels
Construit sur WebDriverJS (Selenium)
Permet de simuler l'intéraction d'un utilisateur avec le navigateur
"Black box testing"

Framework de test

Jasmine

Framework de test JavaScript
Fournit un DSL expressif
Syntaxe "Behavior Driven"
Framework par défaut pour Karma et Protractor

describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});

});

ATDD

Tests unitaires - Squelette

describe('myComponent', function() {

beforeEach(module('myModule'));
beforeEach(inject(...));

it('should do something', function() {
...

});
})

Les fonctions module() et inject() sont
fournies par la librairie angular-mocks
module() permet de charger un module
dans le contexte du test (ne pas confondre
avec angular.module())
inject() permet d'obtenir les composants
requis pour les tests

Tester un Service

describe('myService', function() {

var myService;

beforeEach(module('myModule'));
beforeEach(inject(function(_myService_) {

myService = _myService_;
}));

it('should return true', function() {
var result = myService.getTrue();
expect(result).toBe(true);

});

})

inject() ignore les underscores du paramètre _myService_, ce qui
permet de créer une variable myService sans qu'elle soit masquée

Tester un Controller (sans $scope)

angular.module('app', [])
.controller('GreetingsCtrl', function() {

this.message = "Hello"
})

describe('GreetingsCtrl', function() {
var greetingsCtrl;
beforeEach(module('app'));
beforeEach(inject(function($controller) {
greetingsCtrl = $controller('GreetingsCtrl');
}));
it('should say Hello', function() {
expect(greetingsCtrl.message).toBe("Hello");
});

})

Tester un Controller (avec $scope)

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

$scope.message = "Hello"
})

describe('GreetingsCtrl', function() {
var $scope, greetingsCtrl;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
greetingsCtrl = $controller('GreetingsCtrl', {$scope: $scope});
}));
it('should say Hello', function() {
expect($scope.message).toBe("Hello");
});

})

Tester un Filtre

describe('limitTo', function() {

var limitTo;

beforeEach(module('app'));
beforeEach(inject(function($filter) {

limitTo = $filter('limitTo');
}));

it('should limit array to one element', function() {
var array = ['a', 'b', 'c'];
var result = limitTo(array, 1);
expect(result.length).toBe(1);

});

})

Tester une Directive

angular.module('app', [])

.directive('panel', function() {
return function(scope, element) {
element.addClass('panel');
}

})

describe('panel directive', function() {
var element;

beforeEach(module('app'));
beforeEach(inject(function($compile, $rootScope) {

element = angular.element('<div panel>Some text</div>');
$compile(element)($rootScope)
}));

it('should add class panel to element', function() {
expect(element.hasClass('panel')).toBe(true);

});
})

Tester un service utilisant $http

angular.module('app',[]) describe('Post model tests', function () {
.factory('Post',function($http){
var myService,
var url = ''; $httpBackend,
return { url = 'http://my-web-service.com/posts';

create : function(post){ beforeEach(module('app'));
return $http.post(url,post);
beforeEach(inject(function(_myService_, _$httpBackend_){
} myService = _myService_;
} $httpBackend = _$httpBackend_;
})
}));

afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();

});

it('should create an post', function () {
var postData = {
content: 'a content'
};
$httpBackend.whenPOST(url).respond(postData);
var createdPost;
myService
.create(postData)
.success(function(data){
createdAssessment = data;
});
$httpBackend.expectPOST(url, postData);
$httpBackend.flush();
expect(createdPost).toBeDefined();

});
});

Tests fonctionnels (E2E)

sudo npm install -g protractor

Installation de selenium sudo webdriver-manager update
Démarage de selenium sudo webdriver-manager start

Tests fonctionnels (E2E)

describe('Home screen', function () {
beforeEach(function() {
browser.get('/#');
});
it('should have title Home', function () {
expect(browser.getTitle()).toBe('Home');
});

});

La fonction browser.get() permet d'envoyer le navigateur à
l'URL de l'écran que l'on veut tester.

Simuler le comportement utilisateur

<button id="save-button" ng-click="saveMessage='Saved'">Save</button>
<div>{{ saveMessage }}</div>

describe('Save screen', function () {
beforeEach(function() {
browser.get('/#');
});
it('should display message when save button clicked', function () {
var button = element(by.id('save-button'));
var messageElement = element(by.binding('saveMessage'));
button.click();
expect(messageElement.getText()).toBe('Saved');
});

})

Liste des sélecteurs : https://github.com/angular/protractor/blob/master/docs/api.md#element

Problème

Les tests contiennent beaucoup de code de manipulation du
DOM, ce qui les rendent :

Peu expressifs
Fragiles
Le code des tests est encombré de détails concernant le
DOM, alors qu'il devrait s'intéresser uniquement aux règles
métier .

De subtiles modifications du DOM peuvent rendre de
nombreux tests inopérants, nécessitant un lourd travail de
maintenance des tests

Page Object

Il est possible de créer des objets JavaScript qui fournissent une API
permettant de manipuler chaque vue à tester.

Ces objets encapsulent le code de manipulation du DOM, cachant ainsi les
détails d'implémentation

Si le DOM change, seul le Page Object doit être mis à jour

L'API fournie est à un niveau d'abstraction élevé, adapté à l'écriture et à la
lecture des tests fonctionnels

Page Object - Exemple

function SavePage() {

this.saveButton = element(by.id('save-button'));
this.saveMessageElement = element(by.binding('saveMessage'));

this.get = function () {
browser.get('/#');

};

this.getSaveMessage = function () {
return this.saveMessageElement.getText();

}
}

module.exports = SavePage;

Tester avec un Page Object

var SavePage = require('./SavePage');
describe('Save page', function () {

var page = new SavePage();
beforeEach(function() {

page.get();
});
it('should display message when save button clicked', function () {

page.saveButton.click();
expect(page.getSaveMessage()).toBe('Saved');
});
})

End !

Et après ?

les tuto video d'egghead
ng-newsletter
meetup angular
Angular material
Ionic
Retrouvez les exemples de ce support sur codepen
https://github.com/AmbientIT/AngularJs-exercice
https://github.com/Charl---/formationAngularJs/tree/correction
ng-conf 2015

Support, exemples et exercices conçus par
Mikael Couzic et Charles Jacquin

Annexes

$resource

Niveau d'abstraction plus élevé que $http.
Nécessite d'importer le package ngResource.

Avantages :
Très simple à mettre en place, quelques
lignes suffisent

Inconvénients :
Documentation illisible
Pas de promise jusqu'à la version 1.2


Click to View FlipBook Version