Les languages supporter par AngularJS :
EcmaScript: Une spécification de javascript
EcmaScript 6
Arrow functions
Classes
Collections
get / set transparents
let / const
ES6 modules
// ES6
class Shape {
constructor (id, x, y) {
this.id = id
this.move(x, y)
}
move (x, y) {
this.x = x
this.y = y
}
}
// ES5
var Shape = function (id, x, y) {
this.id = id;
this.move(x, y);
};
Shape.prototype.move = function (x, y) {
this.x = x;
this.y = y;
};(function(){
'use strict';
//code here
})();Installation des outils
Git
NodeJs
Un IDE
Gulp, Bower, Npm, Yeoman, JsLint, BrowserSync
UI frameworks
CSS preprocessor (extension du CSS)
Implémentation en Ruby et C++
Concurrente de SASS
Utilisé par Bootstrap,
implémentation en NodeJs
Pareil que SASS
mais implémentation en NodeJS et sans accolades
Web frameworks
Ecrit en Ruby
Développé par Facebook
Ecrit en Python.
Ressemble à AngularJs
Text
Misko Hevery et Adam Abronsw
<h1>{{ 1+1 }}</h1> // 2
<h1>{{ "Hello " + "world !!!" }}</h1> // Hello world
<h1>{{ "jaafar" | uppercase }}</h1> // JAAFARIgnore les null et undefined => affiche une chaine vide.
Règles:
Les variables des expressions sont évaluées depuis l'objet scope et non l'objet window.
Pour respecter la séparation du code entre les vues et les contrôlleurs, il est impératif de garder les expressions simples.
Il est préférable de faire appel à une fonction exposée par un contrôlleur.
<input type="text" ng-model="ctrl.hello"/> <h1>{{ ctrl.hello }} world !!!</h1>
<input type="text" ng-model="ctrl.myVar"/>
<h1 class="{{ ctrl.myVar }}">Hello world !!!</h1>Le double data-binding d'AngularJS permet de maintenir le modèle en accord avec la vue et vice-versa.
SCOPE
<input type="text" ng-model="ctrl.hello"/>
<h1>{{ ctrl.hello }} world !!!</h1> AngularJS va chercher un attribut nommé 'hello' dans l'objet scope, si celui-ci est trouvé alors la valeur de l'attribut sera afchée sinon une chaîne de caractères vide.
Le scope est ainsi utilisé pour initialiser des valeurs ou récupérer les valeurs modifiées par un utilisateur
SCOPE
Une application est un arbre de modules et de composants
Un arbre de modules
AppModule
CoreModule
SharedModule
FeaturesModule
(function () {
'use strict';
angular
.module('app', []);
})();
AngularJS utilise une logique modulaire pour déclarer les différents composants de l'application
Les librairies externes sont souvent récupérées sous forme de module
(function () {
'use strict';
angular
.module('app')
.controller()
.directive()
.pipe();
})();
Déclaration du module
Déclaration dans le module
facultatifs
3rd party dependencies
(function () {
'use strict';
angular
.module('app')
.controller('AppController', AppController);
function AppController() {
var vm = this;
}
})();
Bonne pratiques : StyleGuide
vm (ViewModel) : une capture de this.
Le this est contextuelle, il peut changer quand on l'appel à l'intérieur d'une fonction.
angular
.module('app')
.component('appComponent', {
templateUrl: 'app/app-component/app.component.html',
controller: AppController,
controllerAs: 'appCtrl'
});
function AppController() {
var vm = this;
vm.hello = 'Hello component World!';
}
Bonne pratiques : StyleGuide
1.5 =<
controllerAs : sucre syntaxique
qui nous évite l'utilisation direct du $scope
Github de la formation
# git config --global http.proxy http://lill.proxy.corp.sopra:8080/
# git clone -b step-00 https://github.com/Jmadih/ng1-tp
# cd ng1-tp
# npm install && npm -g install gulp-cli && npm -g install bower && bower install
# npm install -g gulp
# gulp serve
●une branche d’exercice : step-00
●une branche de solution : step-00-solution
Un concept => une branche
git checkout step-00
Déclarer pour le composant home sous le module ng1tp
Ajouter la variable hello et l'afficher sur le template home.html
AngularJS permet de créer des single page applications même si l'application ne contient qu'une page.
Structurer son application :
Header
Body
Footer
//Etat
$stateProvider.state('contact.detail', {
url: '/contacts/:id',
templateUrl: 'contacts.html',
controller: 'ContactsCtrl as contacts',
resolve: {...}
});
//Route
$routeProvider.when('/contacts/:id', {
templateUrl: 'contacts.html',
controller: 'ContactsCtrl as contacts',
resolve: {...}
});
// Activer un etat dans un controlleur
$state.go('mystate');
// Activer un etat dans un template
<a ui-sref="mystate">Lien</a>
// home.route.js
(function () {
'use strict';
angular
.module('app.home').config(configState);
function configState($stateProvider) {
$stateProvider.state('main.home', {
url: '/home',
views: {
'content@layout': {
templateUrl: 'app/home/home.html',
controller: 'HomeController as homeCtrl'
}
}
});
}
})();
// home.module.js
(function () {
'use strict';
angular
.module('app.home', ['ui.router']);
})();Routes définition
Déclaration
Routes: structurer l'application
(function () {
'use strict';
angular
.module('app.layout').config(configState);
function configState($stateProvider) {
$stateProvider
.state('layout', {
templateUrl: 'app/layout/layout.html'
}).state('main', {
parent: 'layout',
abstract: true,
views: {
menubar: {
templateUrl: 'app/layout/header/menubar/menubar.html',
controller: 'MenubarController as menubar'
},
footer: {
templateUrl: 'app/layout/footer/footer.html'
}
}
});
}
})();
menubar
content
footer
Routes: structurer l'application
<div class="container-fluid">
<header>
<nav class="menubar navbar navbar-inverse navbar-default" ui-view="menubar">
<i>menubar</i>
</nav>
</header>
<div class="main-content" role="main" ui-view="content"><i>content</i></div>
</div>
<footer class="footer minimal" ui-view="footer"><i>footer</i></footer>
menubar
content
footer
git checkout step-01
Suivre l'exemple de l'état ng1tp.homec
//Etat
$stateProvider.state('contact.detail', {
url: '/contacts/:id',
templateUrl: 'contacts.html',
controller: 'ContactsCtrl as contacts',
resolve: {...}
});
//Route
$routeProvider.when('/contacts/:id', {
templateUrl: 'contacts.html',
controller: 'ContactsCtrl as contacts',
resolve: {...}
});
//Etat
$stateProvider.state('contact.detail', {
url: '/contacts/:id',
templateUrl: 'contacts.html',
controller: 'ContactsCtrl as contacts',
resolve: {
contacts: function($http){
return $http.get('/api/v1/contacts'); // promise
},
simple: function(){
return 'simple';
}
}
});
// Controller
function contactDetailController($log, contacts, simple) {
var vm = this;
$log.log(contacts); // promise resolved
$log.log(simple); // simple
}
Angular 1.4.X
//Component
angular
.module('app.contacts')
.component('contactsComponent', {
bindings: {
contacts: '<',
simple: '@'
},
templateUrl: 'app/contacts/contacts.html',
controller: contactsController,
controllerAs: 'contactsCtrl'
});
function contactsController($log) {
var vm = this;
this.$onInit = function () {
$log.log(vm.contacts);
$log.log(vm.simple);
};
}Angular 1.5 <
//Etat
$stateProvider.state('contact', {
url: '/contacts/:id',
templateUrl: 'contacts.html',
component: 'contactsComponent',
resolve: {
contacts: function($http){
return $http.get('/api/v1/contacts'); // promise
},
simple: function(){
return 'simple';
}
}
});
git checkout step-02
Ajouter deux resolvers (dans home.route.js et participants.route.js) qui résoudront la variable 'simple' puis afficher les avec $log.
Déplacer le tableau des participants du composant au resolver sur la route participants.route.js puis l'afficher avec $log.
Bonne pratiques : StyleGuide
angular
.module('app.contacts')
.component('contactsComponent', {
bindings: {
simple: '@',
contacts: '<'
},
templateUrl: 'app/participants/participants.html',
controller: contactsController,
controllerAs: 'contactsCtrl'
});
function contactsController($log, $http, $scope) {
var vm = this;
this.$onInit = function () {
$log.log(vm.simple);
};
}AngularJs va reconnaître les paramètres de la fonction (en appelant toString() sur le contrôleur) puis récupérer les objets auprès des providers (ici $httpProvider)
function ContactsService() {
return {
getContacts: getContacts
}
function handleSuccess(data, status, headers, config) {
// use data, status, headers, config
return data;
}
function handleError(data, status, headers, config) {
// handle errors
}
getContacts() {
return $http
.get(`/api/contacts`)
.success(handleSuccess)
.error(handleError);
}
}Angular 1.4.x
function ContactsService() {
return {
getContacts: getContacts
}
function handleSuccess(data, status, headers, config) {
// use data, status, headers, config
return data;
}
function handleError(data, status, headers, config) {
// handle errors
}
getContacts() {
return $http
.get(`/api/contacts`)
.then(handleSuccess)
.catch(handleError);
}
}Angular 1.6.x
Deprecated
(function () {
'use strict';
angular
.module('app.contacts')
.factory('contactsService', contactsService);
function contactsService() {
return { //revealing module pattern
read: read,
write: write
}
function read { }
function write { }
}
})();
// Controller
angular
.module('app')
.controller('contactsController',
function (contactsService) {
contactsService.read();
contactsService.write();
});(function () {
'use strict';
angular
.module('app.contacts')
.service('contactsService', contactsService);
function contactsService() {
this.read = function read {}
this.write = function write {}
}
})();
// Controller
angular
.module('app')
.controller('contactsController',
function (contactsService) {
contactsService.read();
contactsService.write();
});git checkout step-03
// Démarrer l'application REST
# npm start
node _server/server.js
✔Express server listening on http://localhost:9000/
Une directive est un comportement que vous voulez modifier ou transformer un composant réutilisable de l'application
<html>
<head>
</head>
<body ng-app="myApp">
<section id="todoapp" ng-controller="TodoCtrl">
</section>
</body>
</html>ng-app, ng-controller
framework
<input name="country" ng-model="ctrl.country" />
template
ng-model : controlleur du model
$pristine : permet de savoir si le champ n'a pas été touché
$dirty : inverse de $pristine $valid : savoir si le champ est valide
$invalid : savoir si le champ est invalide
$error : map contenant les erreurs
$untouched : si le champ n'a pas perdu le focus
$touched : si le champ a perdu le focus
template
ng-if, ng-show, ng-hide
<div ng-if="false"> // enleve la div du DOM
Content
</div>
<div ng-if="true"> // remet la div dans le DOM
Content
</div>template
ng-repeat
<li ng-repeat="todo in todos> </li>
<div ng-repeat="todo in todos track by $index">{{ todo }}</div>Chaque élément aura son propre scope exposant les propriétés :
$index : position dans l'itérateur
$first : vrai s'il s'agit du premier élément
$last : vrai s'il s'agit du dernier
$middle : s'il s'agit ni du premier ni dernier élément $even/$odd : vrai si l'élément est pair/impair
template
ng-switch
<div ng-switch="profil">
<span ng-switch-when="ADMIN">Administrateur</span>
<span ng-switch-when="USER">Utilisateur</span>
<span ng-switch-default>Anonyme</span>
</div>Permet d'inclure un template après l'avoir compilé dans un élément du DOM
style
ng-show / ng-hide
<div ng-show="false"> // cache la div
Content
</div>
<div ng-hide="true"> // cache la div
Content
</div>Permet de cacher les éléments avec le style display:none
style
ng-class
.green { color: green;}
.bold { font-weight: bold;}
<input type="checkbox" ng-model="greenChecked"/>
<input type="checkbox" ng-model="boldChecked"/>
<div ng-class="{green: greenChecked, bold: boldChecked }">I'm classy</div>Permet d'appliquer un style dynamiquement
style
ng-disable
<input type="checkbox" ng-model="disable"/>
<button ng-disabled="disable">Disable</button>
{{disable}}
Permet de désactivé un élément dynamiquement
style
ng-readonly
<input type="checkbox" ng-model="isReadOnly"/><br/>
<input type="text" ng-readonly="isReadOnly" />
{{isReadOnly}}Permet de passer l'input en readOnly
Custum
Il existe 4 types :
Custum
<my-directive>load myDirective</my-directive> //Some titleElement
app.directive('myDirective', function() {
return {
template: '<h1>Some title</h1>'
// OR templateUrl: '/my-directive.html'
};
});Custum
<div my-directive></div> //Some titleAttribut
app.directive('myDirective', function() {
return {
template: '<h1>Some title</h1>'
// OR templateUrl: '/my-directive.html'
};
});Custum
<div class=”my-directive: expression;”>My Text</div> // Red when expression = true Class Css
app.directive('myDirective', function() {
return {
restrict: 'C',
link: function (scope, element, attrs) {
element.css('color', 'red');
}
};
});Restrict
Définition de l'implémentation de la directive
restrict : ‘A’ – attribute (default one) | <div my-directive></div>
restrict : ‘C’ – Class | <div class=”my-directive: expression;”></div>
restrict : ‘E’ – element | <my-directive></my-directive>
restrict : ‘M’ – Comment | <!– Directive : my-directive expression; –>Custum
Scope
Pour faire des composants réutilisables, il est possible de les isoler du scope.
app.directive('myDir', function() {
return {
scope: {},
template: '<h1>{{color}} Wont work</h1>'
};
});3 types :
@
=
&
Custum
Scope: @
<my-dir color="red"></my-dir>
app.directive('myDir', function() {
return {
scope: {
myColor: '@color'
},
template: '<h1>{{myColor}}</h1>' //red
};
});Custum
Scope: =
<my-dir obj="obj"></my-dir> // obj.color="red"
app.directive('myDir', function() {
return {
scope: {
myObj: '=obj'
},
template: '<h1>{{myObj.color}}</h1>' // red
};
});Par référence
Custum
Scope: &
// template
<my-dir showAlert="ctrl.showAlert()"></my-dir>
// controller
vm.showAlert = function(){
alert('red');
}
//directive
app.directive('myDir', function() {
return {
scope: {
showAlert: '&'
},
template: '<h1 ng-click="showAlert()>Show alert</h1>' // alert red
};
});Fonction par référence
git checkout step-04
2 nouveaux attributs ont été ajouter au model :
En se basant sur les directives :
- Dupliquer le tableau et ajouter une checkbox pour l'afficher ou le cacher.
- Afficher sur la colonne Mood pour chaque valeur "mood" son icone
- Créer une custum directive "moodDirective" pour factoriser le code
Les filtres permettent de transformer les valeurs d'affichage, de filtrer ou réordonner les éléments d'une collection.
// UNIX syntaxe
{{ expression | uppercase }}
{{ expression | uppercase | trim }}
{{ array | orderBy:'name':true }}
// With JS
function myController($filter, $log){
var toUpper = $filter('uppercase');
$log.log(toUpper('hello')); // HELLO
}Dynamique
<input type="text" ng-model="mysearch"/>
<tr ng-repeat="person in persons | filter:mysearch">
// Sur une propriété spécifique
<input type="text" ng-model="mysearch.name"/>
<tr ng-repeat="person in persons | filter:mysearch" />
<tr ng-repeat="person in persons | filter:{name: 'jaafar'}" />app.filter('makeUppercase', function () {
return function (item) {
return item.toUpperCase();
};
});
{{ 'jaafar' | makeUppercase }} // JAAFARCustum filter
app.filter('startsWithLetter', function () {
return function (items, letter) {
var filtered = [];
return filtered;
};
});
<li ng-repeat="friend in person.friends | startsWithLetter:letter">
{{ friend }}
</li>Custum filter
app.filter('complexFiltre', function () {
return function (items, letter, something, somethingElse) {
var filtered = [];
return filtered;
};
});
<li ng-repeat="friend in person.friends | complexFiltre:letter:something:somethingElse">
{{ friend }}
</li>git checkout step-05
Filtrer les tableaux pour séparer les personnes absentes et présentes (utiliser la propriété absent)
Créer un custum filtre 'UndefinedFilter' qui permet d'afficher 'N/A' si aucune valeur n'est renseigner
<form name="loginForm">
<input type="text" name="user" />
<input type="text" name="password" />
<button>connexion</button>
</form>La directive form instancie un contrôleur FormController : loginForm
Qui est le parent des ngModelControllers : user et password
Pour accéder à un champs :
Chaque controlleur expose les méthodes:
$pristine : si l'utilisateur n'a pas touché le formulaire
$dirty : si l'utilisateur a touché le formulaire
$valid : si l'ensemble des champs du formulaire est valide
$invalid : si un des champs du formulaire est invalide
$error: représente le dictionnaire des erreurs
$submitted : indique si le formulaire
<form name="loginForm">
<input type="email" name="login" required ng-model="ctrlName.login"/>
<span ng-show="loginForm.login.$dirty && loginForm.login.$error.required">
Login is required
</span>
<span ng-show="loginForm.login.$dirty && loginForm.login.$error.email">
Invalid email form
</span>
<input type="password" name="password" ng-model="ctrlName.password"/>
<span ng-show="loginForm.login.$dirty && loginForm.login.$error.password">
Password is required
</span>
<button ng-disabled="loginForm.$invalid">connexion</button>
</form>Validation
Attributs de controles d'input
ng-message
<form name="loginForm" ng-submit="ctrlName.login()">
<input type="email" name="login" required ng-model="ctrlName.login"/>
<div ng-if="loginForm.login.$touched" ng-messages="loginForm.login.$error">
<span ng-message="required">
Login is required
</span>
<span ng-message="email">
Invalid email form
</span>
</div>
<button ng-disabled="loginForm.$invalid">connexion</button>
</form><form name="loginForm" ng-submit="ctrlName.login()">
<input type="email" name="login" required ng-model="ctrlName.login"/>
<span ng-show="loginForm.login.$dirty && loginForm.login.$error.required">
Login is required
</span>
<span ng-show="loginForm.login.$dirty && loginForm.login.$error.email">
Invalid email form
</span>
<button ng-disabled="loginForm.$invalid">connexion</button>
</form>git checkout step-06
Créer un sous composant editParticipantsComponent sous le répertoire participants
Configurer sa route avec l'url '/participants/edit/:id'
Récupérer dans le resolver le participant en question à l'aide d'une fonction (
getParticipant(id)) qui appellera le endpoint'/api/v1/participants/id' sur l'api REST*
*Utiliser $stateParams pour récupérer l'id de la route
git checkout step-06
En se basant sur le code html sur la route 'homec'