Vous êtes sur la page 1sur 16

TP7 – Angular : Intro

1) Historique
2010 : Développé par Google, appelé Angular JS, utilise jQuery

Il a gagné en popularité ⇒ ajout de nouvelles fonctionnalité ⇒ de + en +


complexe

2016 : ré-écriture complète, abandonne jQuery, adopte TypeScript.

Dans la v2.3 il y a des incohérences entre les n° de version des packages


nodes utilisés :

⇒ Passage direct vers la version 4

>>> Angular 4 = Angular 2.4 / Angular 5 = Angular 2.5 ... on dira donc
Angular tout court !

2) Architecture d’une application Angular


Une application Angular est un ensemble de component. Un component
encapsule une vue complète ou une partie d’une vue (ce que voit l’utilisateur),
il est composé de 3 parties :

• HTML : un ensemble de balises pour affiche (render) la vue

• Des données (state)

• Un comportent (behavior) : La logique derrière la vue (exp : ce qui se


passe quand l’utilisateur clique sur un bouton)

Un component peut contenir d’autre components. Par exemple si on veut


développer une application web proche de Facebook en utilisant Angular on
peut la modéliser avec l’arbre de component suivant :

• App (root component)

1 TP7 – Angular : intro changuelSami.wordpress.com


◦ NavBar

◦ SideBar

◦ ContentArea

▪ Posts (liste)

• Post (un seul)

◦ Like

◦ Comment

A la racine on a un component appelé AppComponent. C’est la racine de toute


application Angular : on l’appelle « Root Component ». Il contient 3
component enfants : NavBar, SideBar et ContentArea.

On sépare ainsi car la navigation est logiquement une partie séparée de la


vue : elle possède ses propres balise, état et comportement. On encapsule le
tout dans un compoenent appelé NavBarComponent. Ainsi quand on voudra
modifier ou enrichir la barre de navigation de l’app, on se concentre sur un
petit component contenant des templates légers. On peut aussi intégrer ce
compoenent dans un projet différent : on va juste modifier les lien, mais le
reste (HTML, logique fonctionnelle…) ne change pas (Exp : une partie des
menus et icônes de la suite Office)

3) Environnement de travail
Effectuer les vérifications suivantes :

• $ nodejs -v ⇒ minimum v6.9.x

• $ npm -v ⇒ minimum v3.x.y


• $ eslint -v
• $ tsc -v

Dossier de travail : /bureau/fw-client/

Créer un nouveau projet Angular :


$ sudo ng new ng-hello-world
$ cd ng-hello-world
$ ng serve -o ⇒ o pour « Open in browser » (non obligatoire)
$ code . OU subl .

2 TP7 – Angular : intro changuelSami.wordpress.com


Ajouter le dossier « ng-hello-word » à votre environnement de développement
(Visual studio code, SublimeText3 ou autre) et analyser le dossier /src/app : il
contient un seul component : AppComponent (aucun sous dossier)

On va développer une web-app e-commerce très basique : sur la home on


affichera la liste des produits, sous chaque produit un bouton « ajouter au
panier » : ça sera notre 1er component qui va inclure les balises HTML affichant
les produits et qui va gérer l’événement de click sur le bouton d’ajout au
panier.

Créer un nouveau component appelé ProductComponent :

$ ng g c product ⇐ g pour Generate ; c pour Component

Cette commande va créer le dossier /src/app/product/ et y générer 4 fichiers.


Elle va aussi inscrire (register) le component dans le module « app » :
/src/app/app.module.ts (lignes 6 et 12). Pour le moment retenir qu’un module
regroupe les components liés entre eux.

Ouvrir le code de /src/app/product/product.component.ts et bien noter la ligne 4 :


selector: 'app-product',
C’est un sélecteur CSS, app-product représente un élément portant le même
nom, ça permet d’enrichir les balises HTML puisqu’en ajoutant une balise
<app-product> n’importe où dans un fichie HTML, Angular va le remplacer par
le contenu de ProductComponent.

On a dit que la racine d’une app Angular est AppComponent, supprimer le


contenu de /src/app/app.component.html et insérer ceci :
<app-product></app-product>

Maintenant notre application contient l’arbre de components suivant :

• App

◦ Product

A la racine on a AppComponent qui inclus un enfant : ProductComponent

Sauvegarder, la page sur le navigateur sera actualisé automatiquement et


affichera ceci :

3 TP7 – Angular : intro changuelSami.wordpress.com


Le texte affiché est dans /src/app/product/product.component.html, ajouter
votre nom dans ce fichier

On revient à product.component.ts :
export class ProductComponent implements OnInit {
constructor() { }

ngOnInit() {
}

Le composant est une classe avec deux méthode : Angular va exécuter


ngOnInit après avoir terminé l’appel du constructeur. Pour le moment on va
supprimer ces deux méthodes, et du coup on peut aussi supprimer l’import de
la classe OnInit :
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {

constructor() { }

ngOnInit() {
}

Une classe toute seule ne peut rien afficher, elle permet juste d’effectuer des
traitement. C’est pour ça qu’Angular ajoute la ligne 2:

@Component({ ... })
Cette ligne s’appelle « Décorateur » (decorator) de type Component, c’est lui
qui permet de transformer une simple classe en un component en le liant à un
template HTML et un CSS. Ce décorateur est appelé à la 1ère ligne.

4 TP7 – Angular : intro changuelSami.wordpress.com


4) Property Binding : Propriété liée
En Javascipt ou jQuery on maniuple directement le DOM.

Exp : $("#myElement").text("something")

Avec Angular on ne fait plus ça ! On utilise le property binding : c’est le fait de


créer une variable dans la classe et de lier un élément HTML avec cette
variable (dans un seul sens : classe(component) → vue)

Modifier la classe :
export class ProductComponent {
title = 'Clé USB 16Go';
}
Et modifier la vue (HTML) comme suit :
<p> Produit : {{ title }} </p>

Les doubles accolades {{ et }} sont appelés « Intrepolation » : ça demande


à Angular de remplacer ce qui entre les accolades par la valeur du champ
« title » du component AppComponent

Ajouter une nouvelle ligne à la fin :


<br/>
Produit : <span [innerHTML]=title > </span>

Sauvegarder et tester : on a le même résultat. Ceci est la version Javascript du


nouveau code :
var el = document.getElementById("myElement");
el.innerHTML = "Clé USB 16Go";

L’utilisation les crochets [ et ] s’appelle « Property Binding », ça rend la


lecture du code plus facile dans certains cas :
• <img src="{{imageUrl}}"/>

5 TP7 – Angular : intro changuelSami.wordpress.com


• <img [src]="imageUrl"/> ⇐ Le code est plus court et plus lisible

La seule contrainte ici c’est qu’il faut utiliser des attributs DOM et non pas
HTML (qui ne sont pas toujours identiques). Exp :

• <img [src]="imageUrl"/> ⇐ Ok, car la propriété DOM src existante

• <td [colspan]="n"></td> ⇐ PAS Ok, car colspan n’existe pas en DOM

Solution : <td [attr.colspan]="n"></td> ⇐ Ainsi on dit à Angular de


modifier l’attribut HTML et non pas la propriété DOM

ATTENTION : le property biding et l’interpolation (dont le fonctionnement est le


même) fonctionnent dans UN SEUL SENS : depuis le component vers le
template.

Dans la classe ajouter un nouveau champ :


export class ProductComponent {
title = 'Clé USB 16Go';
itemCount = 0;
}
Et modifier le template :
<p>Produit : {{ title }}</p>
<p>Qte : {{ itemCount }}</p>

5) Event Binding : Événement lié


Il s’agit de lier un événement du DOM à une méthode du component. Ajouter
ceci à la vue :
<p>Produit : {{ title }}</p>
<p>Qte : {{ itemCount }}</p>
<button (click)="addItem()">Ajouter au panier</button>

Ajouter ensuite la nouvelle méthode à la classe (à compléter) :


export class ProductComponent {
title = 'Clé USB 16Go';
itemCount = 0;

addItem() {
this.___++;
}

6 TP7 – Angular : intro changuelSami.wordpress.com


}

Tester en cliquant sur le bouton plusieurs fois : à chaque fois la quantité


augmente, et ce grâce au Property Binding (affichage) ainsi qu’à l’Event
Binding (click sur le bouton)

Pour résumer :

6) Les directives
Nous allons afficher une liste de produits sous forme de plusieurs <div>, on va

Une directive modifie la structure du DOM (ajout ou suppression d’un


élément), elle doit être précédée par « * »

Dans le component ajouter un champ de type tableau contenant les produits à


afficher :
products = ['Clé USB 2Go', 'Clé USB 4Go', 'Souris bluetooth'] ;

Et modifier le template comme ceci (à compléter)


<div *ngFor="let product of ___">
<p>Produit : {{ ___ }}</p>
<p>Qte : {{ itemCount }}</p>
<button (click)="addItem()">Ajouter au panier</button>
</div>

Voilà le résultat final :

7 TP7 – Angular : intro changuelSami.wordpress.com


Cliquer sur le 1er bouton « Ajouter au panier » : la quantité augmente pour
tous les produits en même temps : ce n’est pas bon !!!

Solution : remplacer le tableau des produit par un tableau d’objets :


products = [
{ qty: 0, name: 'Clé USB 2Go' },
{ qty: 0, name: 'Clé USB 4Go' },
{ qty: 0, name: 'Souris bluetooth' }
];

Dans le template, changer le contenu de la boucle (à compléter):


<div *ngFor="...">
<p>Produit : {{ product.name }}</p>
<p>Qte : {{ product.___ }}</p>
<button (click)="addItem()">Ajouter au panier</button>
</div>
Sauvegarder et tester : l’affichage est bon, mais le bouton
« Ajouter au panier » ne fait plus rien ! Modifier le template :
<button (click)="addItem(product)">Ajouter au panier</button>

Enfin changer le component comme suit (à compléter) :


addItem(product) {

8 TP7 – Angular : intro changuelSami.wordpress.com


product.___++;
}

Sauvegarder et vérifier que le bouton modifie la quantité du produit


lié (et non pas les autres)

7) Les services
/!\ Visual Code Studio > Installer l’extension « Auto import »

La liste des produits est statique, dans une application réelle la liste provient
depuis un serveur distant. On peut implémenter l’appel au serveur dans
component mais ce n’est pas bon :

• On aura un couplage fort entre le component et le serveur

• Si on a besoin de ses données dans un autre component il faudra


dupliquer le code

• Un component ne doit implémenter que la logique de présentation : ce


qui se passe quand on clique sur bouton, quand on sélectionne un
élément depuis une liste ... c’est tout !

Créer le fichier /app/product/product.service.ts dont le code est le suivant :


export class ProductService {

constructor() { }
getProducts(){
return [
{ qty: 0, name: 'Clé USB 2Go' },
{ qty: 0, name: 'Clé USB 4Go' },
{ qty: 0, name: 'Souris bluetooth' }
];
}
}

Il s’agit d’une simple classe Typescript : pas de directive, par d’import … Pour
le moment notre service renvoi des données statiques.

Dans le component importer (si nécessaire) le service :


import { ProductService } from './product.service';

On va implémenter le constructeur avec dedans une instance de la classe


ProductService. Voici le nouveau component (à compléter) :

9 TP7 – Angular : intro changuelSami.wordpress.com


export class ProductComponent {
// Tableau des produit, vide pour le moment
products;
constructor() {
let service = new ProductService();
// Remplir le tableau via la méthode du service
this.___ = ___.getProducts();
}

addItem(product) {
product.qty++;
}
}

8) Exercice
Modifier la vue et le component afin d’avoir le
nombre total des produits.

ATTENTION : ce nombre doit être dynamique


(change en fonction du nombre des produits, et
NON PAS des quantité). On rappelle que pour avoir
la taille d’un tableau on fait : var taille = tableau.length ;

9) Utiliser Bootstrap
Dans le dossier racine du projet angular lancer ceci :
$ npm install bootstrap
Ajouter ceci dans /src/styles.css :
@import "~bootstrap/dist/css/bootstrap.css"
/!\ Bootstrap sera appliqué à tous les templates de tous les component du
projet !
Sauvegarder et vérifier que l’affichage a bien changé :

10 TP7 – Angular : intro changuelSami.wordpress.com


On remarque que le contenu est collé à droite, changer /src/index.html :

<body class="container">
<app-root></app-root>
</body>
Vérifier que le contenu n’est plus collé à droite.

10) Class Binding


Appliquer une classe bootstrap à la quantité comme suit :
<div *ngFor="let product of products">
<p>Produit : {{ product.name }}</p>
<p class="badge">Qte : {{ product.qty }}</p>
<button (click)="addItem(product)">Ajouter au panier</button>
</div>
On voudrait que si la quantité commandée dépasse « 2 » la couleur de fond de
la quantité change. Voici les différentes classes proposées par BS :

https://getbootstrap.com/docs/4.0/components/badge/
Modifier le code comme ceci :

<p class="badge" [class.badge-warning]="product.qty > 2">Qte :


{{ product.qty }}</p>
Vérifier que le code fonctionne bien :

11 TP7 – Angular : intro changuelSami.wordpress.com


/!\Exercice : ajouter ce qu’il faut pour appliquer le badge « danger » quand
la quantité dépasse « 5 » Ci-dessous le résultat souhaité :

11) Style Binding


C’est le même principe que le Class Binding mais s’applique aux attributs CSS.
Exp : on voudrait que le nom du produit soit en fond rouge si la quantité
dépasse 2. Modifier le code comme suit (à compléter) :
<div *ngFor="let product of products">
<p [style.backgroundColor]="___ > 2 ? '___' : 'none'">Produit :
{{ product.name }}</p>

12) Exercice
Ajouter en bas le code HTML suivant :
Panier : <span class="badge badge-info">{{total_qty}}</span>
Dans le component ajouter ceci :
total_qty = 0;
Modifier la fonction addItem() pour mettre à jour le nombre total des produits.
Voici le code à compléter :
addItem(product) {
product.qty++;

let total_qty = 0;
for (let i = 0; i < this.products.length; i++) {
total_qty += ___;
}
this.___ = ___;
}

13) Exercice : Two-way Binding


Générer un noveau component « heroes » (via la ligne de commande)

Insérer le CSS suivant :

12 TP7 – Angular : intro changuelSami.wordpress.com


.selected{background-color:#CFD8DC!important;color:#fff}
.heroes{margin:0 0 2em;list-style-type:none;padding:0;width:15em}
.heroes li{cursor:pointer;position:relative;left:0;background-
color:#EEE;margin:.5em;padding:.3em 0;height:1.6em;border-radius:4px}
.heroes li.selected:hover{background-color:#BBD8DC!important;color:#fff}
.heroes li:hover{color:#607D8B;background-color:#DDD;left:.1em}
.heroes .text{position:relative;top:-3px}.heroes .badge{display:inline-
block;font-size:small;color:#fff;padding:.8em .7em 0;background-
color:#607D8B;line-height:1em;position:relative;left:-1px;top:-
4px;height:1.8em;margin-right:.8em;border-radius:4px 0 0 4px}

Créer un service ayant une méthode « getHeroes() » qui renvoie un tableau de


3 objets de la structure suivante : {id, name}

Dans le constructeur du component appeler la méthode getHeroes() afin de


remplir la variable locale « heroes »

Compléter le template ci-dessous afin d’afficher tous les héros :


<h1>List of heroes</h1>
<ul class="heroes">
<li>
<span class="badge">XXX</span>YYY
</li>
</ul>
XXX est à remplacer par l’identifiant de l’héro, YYY par son nom.

Modifier le template du Root Component afin d’afficher le nouveau component,


tout en gardant l’ancien travail :
<app-product></app-product>
<hr/>
<___></___>

Vérifier que l’affichage est similaire à ceci:

On remarque que l’affichage n’est pas bon, c’est parce que Bootstrap est
appliqué à tous les component, y compris celui qu’on vient de créer ! Pour
appliquer Bootstrap uniquement à l’ancien component « product » supprimer

13 TP7 – Angular : intro changuelSami.wordpress.com


l’appel à Bootstrap depuis /src/styles.css et l’ajouter dans
/src/app/product/product.component.css, vérifier le résultat final :

On veut que le clic sur un héro affiche ses détails. Ajouter le code suivant :
selectedHero;
selectHero(hero) {
this.selectedHero = hero;
}

Modifier le template afin que le clic sur une balise « li » appelle la méthode
précédente en lui passant comme paramètre l’héro respectif.

Ajouter ensuite ce code (à la fin) et le compléter afin qu’il affiche les données
de l’héro sélectionné (selectedHero) :
<div id="details">
<h2>Details</h2>
ID : XXX <br>
Name = <input type="text" value="YYY" />
</div>

Vérifier que le code est fonctionnel :

14 TP7 – Angular : intro changuelSami.wordpress.com


On voudrait que l’héro cliqué dans la liste soit bien mis en évidence, modifier le
template pour appliquer la classe « selected » si l’héro de la boucle est égale
(==) à l’héro sélectionné (voir le point 10.Class Binding) :
<li … [___._____]="___ == ____"> … </li>

Voici le résultat final :

Si on change le nom d’un héro, la liste n’est pas mise à jour ! Pour le faire doit
lier l’input avec l’attribut « hero » mais dans les DEUX SENS (actuellement il
est fait dans un seul sens), modifier le template comme suit :
Name = <input type="text" [(ngModel)]="selectedHero.name" />

La syntaxe du two-way binding est [(x)] : elle combine les crochets [] du


preperty binding et les parenthèses () de l’event binding. Pour mémoriser
l’order, interpréter [()] comme une banane dans une caisse : Banana in a box

Vérifier que la modification du nom depuis le champ texte s’applique aussi


dans la liste :

15 TP7 – Angular : intro changuelSami.wordpress.com


14) Les conditions
Au chargement de la page le bloc « Details » est vide, on va le rendre caché et
ne l’afficher que s’il y a un héro sélectionné, on utilisera la directive ngFor.
Sachant que cette directive modifie le DOM, compléter le code suivant :
<div ___="selectedHero != null">
<h2>Details</h2>

On peut simplifier le code :


"selectedHero != null" ⇒ "selectedHero"

Sources :
https://angular.io/guide/quickstart
https://programmingwithmosh.com/angular/angular-4-tutorial/
http://www.learn-angular.fr/les-modules-angular/

16 TP7 – Angular : intro changuelSami.wordpress.com

Vous aimerez peut-être aussi