________ ___ __ ________ __ __ __ ________ _______
| || \ | || || | | || | | || \
| __ || \ | || _____|| | | || | | __ || __ |
| |__| || \| || | ___ | | | || | | |__| || |__| _|
| __ || |\ || |_|_ || |__| || |_____ | __ || __ \
| | | || | \ || || || || | | || | | |
|__| |__||__| \___||________||________||________||__| |__||__| |__|
Cours Angular.js
angular.txt 29/09/2020
ultimecool.com
CHAPITRE 1 - Angular Généralités 1.1. Introduction 1.2. Commencez avec Angular 1.3. Construisez une application Angular 1.4. Créez un component 1.5. Gérez des données dynamiques 1.6. Structurez le document avec des Directives 1.7. Modifiez les données en temps réel avec les Pipes 1.8. Entraînez-vous en créant une application de type blog 1.9. Améliorez la structure du code avec les Services 1.10. Gérez la navigation avec le Routing 1.11. Observez les données avec RxJS 1.12. Écoutez l'utilisateur avec les Forms - méthode template 1.13. Écoutez l'utilisateur avec les Forms - méthode réactive 1.14. Interagissez avec un serveur avec HttpClient 1.15. Créez une application complète avec Angular 1.16. Les règles de développement Angular 1.17. Conclusion
Angular est une plateforme de développement qui permet de créer des applications web dynamiques et immersives. Dans ce cours, vous apprendrez rapidement à créer les composantes de base d'une application Angular, avant d'enrichir vos applications en approfondissant vos connaissances de ce framework. Vous apprendrez également à faire communiquer votre application avec un backend afin de créer une application web complète.
Il y a plusieurs frameworks JavaScript très populaires aujourd’hui : Angular, React, Ember, Vue… les autres frameworks marchent très bien, ont beaucoup de succès et sont utilisés sur des sites extrêmement bien fréquentés, React et Vue notamment. Angular utilise le TypeScript plutôt que JavaScript pur ou le mélange JS/HTML de React. Angular est géré par Google, donc l’équipe de développement du framework est excellente. Le TypeScript permet un développement beaucoup plus stable, rapide et facile. Le framework Ionic permet le développement d’applis mobiles multi-plateformes à partir d’une seule base de code. Angular est un choix très pertinent pour le développement frontend.
Le CLI, “Command Line Interface” (un outil permettant d'exécuter des commandes depuis la console), d’Angular est l’outil qui vous permet d’exécuter des scripts pour la création, la structuration et la production d’une application Angular. Vous devez installer node.js, npm, et angular/cli sur votre machine :
https://nodejs.org/en/download/
npm install -g npm@latest
npm install -g @angular/cli
Pour créer un nouveau projet Angular, naviguez vers le dossier souhaité depuis une ligne de commande et saisissez la commande suivante :
ng new mon-premier-projet cd mon-premier-projet ng serve --open
votre navigateur se lancera automatiquement avec le message "Welcome to app!!" et le logo Angular sur l'adresse localhost:4200. Mieux vaut utiliser Chrome comme navigateur par défaut, pour le développement, car les Developer Tools sont très complets, et il y a la possibilité d'installer Augury, un plugin Chrome spécifique pour le développement Angular.
Pour développer, utiliser un éditeur comme Atom ou Sublime Text, ou un IDE comme VS Code ou WebStorm. Pour les screencast et screenshot, WebStorm a beaucoup de fonctionnalités spécifiques pour le développement JavaScript/TypeScript. On utilisera les styles en SCSS, et Bootstrap qui facilite la mise en page.
créez un nouveau projet: ng new mon-projet-angular --style=scss --skip-tests=true cd mon-projet-angular
pour installer Bootstrap dans votre projet. tapez : npm install bootstrap@3.3.7 --save
Cette commande téléchargera Bootstrap et l'intégrera dans le " package.json " du projet. Ouvrez le fichier angular.json du dossier source de votre projet. Dans la section "architect/build/options", modifiez l'array styles comme suit:
"styles": [ "node_modules/bootstrap/dist/css/bootstrap.css", "src/styles.scss" ]
Lancer le serveur de développement local (il faudra le couper avec Ctrl-C et le relancer s'il tournait déjà pour que les changements prennent effet) : ng serve
Une fois le projet compilé, ouvrez votre navigateur à lage page "localhost:4200" et vous y verrez l'application. Laisser tourner le serveur de développement en fond. À chaque fois que vous enregistrez des modifications au projet, le CLI met à jour le serveur. Les components sont les composantes de base d'une application Angular : une application est une arborescence de plusieurs components. La structure du code est similaire à react avec les sous dossiers: node_modules, e2e, src. Ouvrez src\index.html :
cette balise vide
app.component.ts
Ici, à l'intérieur du décorateur @Component() , vous trouvez un objet qui contient les éléments suivants :
selector: nom utilisé comme balise HTML pour afficher ce component, comme avec "app-root". Ce nom doit être unique et ne doit pas être un nom réservé HTML de type "div", "body" etc. On utilisera donc très souvent un préfixe comme app
templateUrl : le chemin vers le code HTML à injecter ;
styleUrls : un tableau contenant un ou plusieurs chemins vers les feuilles de styles qui concernent ce component ;
Quand Angular rencontre la balise
Pour créer un nouveau component à l'aide du CLI d'Angular, tapez la commande : ng generate component mon-premier le CLI vat créer un nouveau sous-dossier "mon-premier", avec un fichier template une feuille de styles, un fichier component et un fichier spec : il s'agit d'un fichier de test que vous pouvez supprimer. Le CLI nous prévient également qu'il a mis à jour le fichier app.module.ts :
Le CLI a ajouté MonPremierComponent à l'array declarations de votre module. Il a également ajouté le statement import en haut du fichier. Ce sont des étapes nécessaires pour que vous puissiez utiliser votre component.
mon-premier.component.ts
Vous constaterez que le CLI a créé un sélecteur : app-mon-premier. Nous pouvons donc ajouter ce sélecteur dans notre code pour y insérer ce component. Modifier le composant racine de l'application:
app.component.html
Dans votre navigateur, vous verrez le même titre qu'avant et, sur une deuxième ligne, le texte "mon-premier works" (texte par défaut créé par le CLI) que vous trouverez dans mon-premier.component.html:
mon-premier works!
L'intérêt d'utiliser Angular est de pouvoir gérer le DOM (Document Object Model: les éléments HTML affichés par le navigateur) de manière dynamique, et pour cela il faut utiliser la liaison de données, ou "databinding". Le databinding, c'est la communication entre votre code TypeScript et le template HTML qui est montré à l'utilisateur. Cette communication est divisée en deux directions :
les informations venant de votre code qui doivent être affichées dans le navigateur, comme par exemple des informations que votre code a calculé ou récupéré sur un serveur. Les deux principales méthodes pour cela sont le "string interpolation" et le "property binding" ;
les informations venant du template qui doivent être gérées par le code : l'utilisateur a rempli un formulaire ou cliqué sur un bouton, et il faut réagir et gérer ces événements. On parlera de "event binding" pour cela.
Il existe également des situations comme les formulaires, par exemple, où l'on voudra une communication à double sens : on parle donc de "two-way binding".
Interpolation
L'interpolation est la manière la plus basique d'émettre des données issues de votre code TypeScript.
Créez maintenant un nouveau component AppareilComponent : ng generate component appareil
appareil.component.html
Et modifier le composant racine de l'application:
app.component.html
Vous allez maintenant utiliser l'interpolation pour commencer à dynamiser vos données. Modifiez le composant appareil.component.html ainsi :
Ici, vous trouvez la syntaxe pour l'interpolation: les doubles accolades {{ }}. Ce qu'il y a entre les doubles accolades correspond à l'expression TypeScript que nous voulons afficher, l'expression la plus simple étant une variable. Dans appareil.component.ts :
appareil.component.ts
appareil.component.html
Voilà ! Vous avez maintenant une communication entre votre code TypeScript et votre template HTML.
Property binding
La liaison par propriété ou "property binding" est une autre façon de créer de la communication dynamique entre votre TypeScript et votre template : en modifiant dynamiquement les propriétés d'éléments du DOM en fonction de données dans votre TypeScript.
Ex: La propriété disabled permet de désactiver le bouton. Afin de lier cette propriété au TypeScript, il faut le mettre entre crochets [] et l'associer à la variable ainsi :
Modifier le composant racine de l'application:
app.component.html
app.component.ts
Le bouton s'active au bout de quatre secondes. Pour l'instant le bouton ne fait rien : vous découvrirez comment exécuter du code lorsque l'utilisateur cliquera dessus avec la liaison des événements, ou "event binding".
event binding
De manière générale, vous pouvez lier du code à n'importe quelle propriété ou événement des éléments du DOM. Pour plus d'informations, vous pouvez consulter le Mozilla Developer Network ou W3Schools, par exemple.
Modifier le composant racine de l'application:
app.component.html
app.component.ts
Two-way binding
La liaison à double sens (ou two-way binding) utilise la liaison par propriété et la liaison par événement en même temps ; on l'utilise, par exemple, pour les formulaires, afin de pouvoir déclarer et de récupérer le contenu des champs, entre autres.
Pour pouvoir utiliser le two-way binding, il vous faut importer FormsModule depuis "@angular/forms" dans votre application. Vous pouvez accomplir cela en l'ajoutant au tableau d'imports de votre AppModule (sans oublier d'ajouter le statement import correspondant en haut du fichier) :
app.module.ts
Le two-way binding emploie le mélange des syntaxes de property binding et d'event binding : des crochets et des parenthèses [()] . Par exemple, ajoutez un "input" dans votre template appareil.component.html et liez-le à la variable appareilName en utilisant la directive ngModel:
appareil.component.html
Dans votre template, vous verrez un "input" par appareil. Le nom de l'appareil est déjà indiqué dedans, et si vous le modifiez, le contenu du "h4" est modifié avec. Ainsi vous voyez également que chaque instance de AppareilComponent est entièrement indépendante une fois créée : le fait d'en modifier une ne change rien aux autres. Ce concept est très important, et il s'agit de l'une des plus grandes utilités d'Angular.
Propriétés personnalisées
Il est possible de créer des propriétés personnalisées dans un component afin de pouvoir lui transmettre des données depuis l'extérieur.
Pour l'application des appareils électriques, il serait intéressant de faire en sorte que chaque instance d'AppareilComponent ait un nom différent qu'on puisse régler depuis l'extérieur du code. Pour ce faire, il faut utiliser le décorateur @Input() en remplaçant la déclaration de la variable appareilName : N'oubliez pas d'importer Input depuis @angular/core en haut du fichier !
appareil.component.ts
Modifier le composant racine de l'application:
app.component.ts
appareilTwo = 'Frigo';
app.component.html
Pour passer un string par le property binding, il faut le mettre entre
apostrophes ex:
Les directives sont des instructions intégrées dans le DOM que vous utiliserez
presque systématiquement quand vous créerez des applications Angular. Quand
Angular lit votre template et rencontre une directive qu'il reconnait, il suit
les instructions correspondantes. Il est possible de créer ses propres
directives. Les directives structurelles Ce sont des directives qui, modifient la structure du document. Par ex:
- *ngIf , pour afficher des données de façon conditionnelle,
- *ngFor , pour itérer des données dans un array, par exemple. Un composant auquel on ajoute la directive *ngIf="condition" ne s'affichera que
si la condition est "truthy" (elle retourne la valeur true où la variable
mentionnée est définie et non-nulle), comme un statement if classique. Pour une démonstration simple, ajoutez une balise "div" rouge qui ne s'affichera
que si l'appareil est éteint : appareil.component.html Lorsque l'on ajoute la directive *ngFor="let obj of myArray" à un component,
Angular itérera l'array myArray et affichera un component par objet obj .
On va modifier la façon dont votre application génère des appareils électriques.
On peut imaginer que votre application récupère, depuis un serveur, un tableau
contenant tous les appareils et leurs états. Pour l'instant, créez ce tableau
directement dans AppComponent (composant racine de l'application) : app.component.ts app.component.html Le statement let appareil of appareils, comme dans une for loop classique, itère
pour chaque élément appareil (nom arbitraire) de l'array appareils. Après cette
directive, vous pouvez maintenant utiliser l'objet appareil, à l'intérieur de
cette balise HTML. Vous pouvez donc utiliser le property binding, et y passer
les propriétés name et status de cet objet. Les directives par attribut Les directives par attribut modifient le comportement d'un objet déjà existant.
Vous avez déjà utilisé une directive de ce type sans le savoir : la directive
ngModel que vous avez employée pour le two-way binding, qui modifie la valeur du
"input" et répond à tout changement qu'on lui apporte. Voici 2 autres exemples
très utile : ngStyle et ngClass, qui permettent d'attribuer des styles ou des
classes de manière dynamique. appareil.component.ts Que ce soit pour ngStyle ou pour ngClass, les objets JavaScript peuvent être des
variables, valables dans votre TypeScript qui seront ensuite référencées par la
directive, par exemple : [ngClass]="myClassObject" . Les pipes prennent des données en input, les transforment, et puis affichent les
données modifiées dans le DOM. Il y a des pipes fournis avec Angular, et vous
pouvez également créer vos propres pipes si vous en avez besoin. Je vous propose
de commencer avec les pipes fournis avec Angular. Utilisez et paramétrez les Pipes Un pipe que l'on utilise très souvent est DatePipe, qui analyse des objets JS de
type Date et qui les affiche d'une manière plus lisible. app.component.ts app.component.html Le pipe async est un cas particulier mais très utile dans les applications Web,
car il permet de gérer des données asynchrones, par exemple des données que
l'application doit récupérer sur un serveur. Dans les chapitres suivants, vous
apprendrez à communiquer avec un serveur extérieur, mais pour l'instant, vous
allez simuler ce comportement en créant une Promise qui va se résoudre au bout
de quelques secondes. Modifiez lastUpdate comme suit : Si vous enregistrez le fichier, l'application vous créera une erreur :
Error: InvalidPipeArgument: '[object Promise]' for pipe 'DatePipe' En effet, au moment de générer le DOM, lastUpdate est encore une Promise et
n'a pas de valeur modifiable par les pipes. Il nous faut donc ajouter AsyncPipe
en début de chaîne pour dire à Angular d'attendre l'arrivée des données avant
d'exécuter les autres pipes : Mis à jour : {{ lastUpdate | async | date: 'yMMMMEEEEd' | uppercase }}1.6. Structurez le document avec des Directives
<li class="list-group-item">
<div style="width:20px;height:20px;background-color:red;"
*ngIf="appareilStatus === 'éteint'"></div>
<h4>Appareil : {{ appareilName }} -- Statut : {{ getStatus() }}</h4>
<input type="text" class="form-control" [(ngModel)]="appareilName">
</li>
export class AppComponent {
isAuth = false;
appareils = [
{
name: 'Machine à laver',
status: 'éteint'
},
<h2>Mes appareils</h2>
<ul class="list-group">
<app-appareil *ngFor="let appareil of appareils"
[appareilName]="appareil.name"
[appareilStatus]="appareil.status"></app-appareil>
</ul>
<h4 [ngStyle]="{color: getColor()}">Appareil : {{ appareilName }}
-- Statut : {{ getStatus() }}</h4>
getColor() {
if(this.appareilStatus === 'allumé') {
return 'green';
} else if(this.appareilStatus === 'éteint') {
return 'red';
}
}
?```javascript
_appareil.component.html_
?```html
<li [ngClass]="{'list-group-item': true,
'list-group-item-success': appareilStatus === 'allumé',
'list-group-item-danger': appareilStatus === 'éteint'}">
<div style="width:20px;height:20px;background-color:red;"
*ngIf="appareilStatus === 'éteint'"></div>
<h4 [ngStyle]="{color: getColor()}">
Appareil : {{ appareilName }} -- Statut : {{ getStatus() }}</h4>
<input type="text" class="form-control" [(ngModel)]="appareilName">
</li>
1.7. Modifiez les données en temps réel avec les Pipes
export class AppComponent {
isAuth = false;
lastUpdate = new Date();
<h2>Mes appareils</h2>
<p>Mis à jour : {{ lastUpdate | date: 'short' }}</p>
<p>Mis à jour : {{ lastUpdate | date: 'yMMMMEEEEd' }}</p>
<p>Mis à jour : {{ lastUpdate | date: 'yMMMMEEEEd' | uppercase }}</p>
lastUpdate = new Promise((resolve, reject) => {
const date = new Date();
setTimeout(
() => {
resolve(date);
}, 2000
);
});
Vous allez créer une application simple de type blog. Cette application va afficher les posts du blog, et chaque post aura un bouton permettant de "love it" ou de "don't love it". Chaque post aura la forme suivante :
post: {
title: string,
content: string,
loveIts: number,
created_at: Date
}
Je vous conseille d'utiliser Bootstrap pour cet exercice. Si vous créez des list-group-item dans un list-group, vous avez les classes list-group-item-success et list-group-item-danger pour colorer les item.
En termes de structure :
votre AppComponent contiendra le tableau des posts, et il le passera à un composant PostListComponent
votre PostListComponent affichera un PostListItemComponent pour chaque post dans l'array qu'il a reçu
chaque PostListItemComponent affichera le titre, le contenu et la date de création du post dans le template
les PostListItemComponent auront des boutons qui permettent d'augmenter et de diminuer le nombre de loveIts — cette modification aura uniquement un effet sur le component, et n'a pas besoin d'être remontée au component parent
Bonus : créez un type pour les post, appelé Post, afin de rendre votre code plus lisible. Vous pouvez même y intégrer un constructeur qui attribue en automatique la date !
Quelques astuces
Pensez aux différents types de databinding — comment passer des données d'un component à un autre, comment afficher des données dans le template et comment réagir à un événement venant du template
Pensez aux directives structurelles comme *ngFor , et également aux directives par attribut comme ngClass
Pensez aux Pipes pour la transformation de données, notamment pour la date
Mettre en place la structure d'un projet Angular Récupérez le projet Angular. Lancez un terminal et allez à la racine du projet. Saisissez les commandes npm install puis ng serve .
Le serveur de développement s'est lancé lorsque vous avez exécuté ng serve après un npm install .
PostListComponent se trouve dans un sous-dossier du dossier app.
PostListItemComponent se trouve soit dans un sous-dossier du dossier app, soit dans un sous-dossier du dossier post-list".
Bootstrap est installé dans les node_modules et est référencé correctement dans .angular-cli.
Mettre en oeuvre les templates Angular avec l'interpolation et les pipes
Vous allez évaluer la capacité à mettre en oeuvre les templates Angular.
Les titres, textes et dates des posts sont affichés avec le string interpolation {{ }}
La directive *ngFor est utilisée pour afficher les PostListItemComponent à l'intérieur de PostListComponent
La DatePipe est utilisée correctement pour l'affichage de la date des posts
Les couleurs demandées sont respectées (vert si nombre de like positif, rouge si négatif)
Passer des données entre components Angular avec le databinding
Vous allez évaluer la gestion des données dynamiques dans l'application.
L'array des posts est passé de AppComponent à PostListComponent avec le property binding (avec @Input() )
PostListItemComponent reçoit ses données depuis PostListComponent avec le property binding
Les boutons du template emploient l'event binding pour appeler leur méthode, modifiant l'élément correspondant dans l'array de posts
un service permet de centraliser des parties de votre code et des données qui sont utilisées par plusieurs parties de votre application ou de manière globale par l'application entière. Pour être utilisé dans l'application, un service doit être injecté, et le niveau choisi pour l'injection est très important. Il y a trois niveaux possibles pour cette injection :
dans AppModule : ainsi, la même instance du service sera utilisée par tous les components de l'application et par les autres services ;
dans AppComponent : comme ci-dessus, tous les components auront accès à la même instance du service mais non les autres services ;
dans un autre component : le component lui-même et tous ses enfants (c'est-à- dire tous les components qu'il englobe) auront accès à la même instance du service, mais le reste de l'application n'y aura pas accès.
Pour les exemples de ce cours, vous injecterez les services dans AppModule pour rendre disponible une seule instance par service à toutes les autres parties de votre application.
Créez maintenant un sous-dossier services dans app , et créez-y un nouveau fichier appelé appareil.service.ts, avec une classe vide, puis injecter ce service dans AppModule en l'ajoutant à l'array providers.
appareil.service.ts
app.module.ts
Angular crée maintenant une instance du service AppareilService pour l'application entière. Pour l'intégrer dans un component, on le déclare comme argument dans son constructeur. Intégrez-le dans AppComponent (avec l'import).
app.component.ts
Maintenant, dans AppComponent , vous avez un membre appelé appareilService qui correspond à l'instance de ce service que vous avez créé dans AppModule .
mettre le tableau appareils depuis AppComponent dans AppareilService et, et déclarez appareils simplement comme un array de type any :
Il faut maintenant que AppComponent puisse récupérer les informations stockées dans AppareilService. Pour cela, vous allez implémenter la méthode ngOnInit().
ngOnInit() correspond à une "lifecycle hook". La méthode ngOnInit() est exécutée une fois par instance, au moment de la création du component par Angular, et après son constructeur. On l'utilise très souvent pour initialiser des données une fois le component créé.
Ensuite, dans la déclaration de classe AppComponent , vous allez implémenter l'interface OnInit (en l'important depuis @angular/core en haut) :
import { Component, OnInit } from '@angular/core'; export class AppComponent implements OnInit {
Vous pouvez maintenant récupérer les informations depuis AppareilService dans la méthode ngOnInit() :
La liaison directe à un array comme ici n'est généralement pas un best practice. Nous verrons les meilleures méthodes plus tard dans ce cours ! Votre application devrait fonctionner comme avant, mais votre code est maintenant plus modulaire, et ce sera plus facile d'ajouter des fonctionnalités. Par exemple switchOnAll() et switchOffAll(). Commencez par préparer ces méthodes dans AppareilService :
Puis ajoutez un deuxième bouton dans le template de AppComponent :
app.component.html
Enfin, il ne reste plus qu'à capturer les événements click dans AppComponent pour ensuite déclencher les méthodes dans AppareilService. Commencez déjà par:
app.component.ts
Vos boutons allument et éteignent tous les appareils grâce à la communication entre votre AppComponent et votre AppareilService. Cela permet de créer de la communication entre vos composants, surtout entre les enfants vers leur parent.
Faites communiquer vos components
AppareilService fournit les données sur les appareils à AppComponent. Ensuite, AppComponent génère trois instances de AppareilComponent selon ces données. Il n'y a actuellement aucune communication entre les components enfants et leur parent. Vous pouvez modifier cela en intégrant AppareilService dans les AppareilComponent et en créant des méthodes qui permettent de modifier un appareil à la fois.
Dans un premier temps, il faudra que chaque instance de AppareilComponent puisse dire à AppareilService à quel membre de l'array appareils elle correspond. Heureusement, Angular nous permet de faire ça facilement. Dans la directive *ngFor , ajoutez "let i = index" : Cette commande rend disponible l'index de l'objet appareil dans l'array appareils
app.component.html
appareil.component.ts
Dans AppareilComponent , vous allez d'abord intégrer le service AppareilService, en l'important en haut du fichier. Puis vous allez préparer la méthode qui, en fonction du statut actuel de l'appareil, l'allumera ou l'éteindra :
constructor(private appareilService: AppareilService) { }
Le nom onSwitch() ici est choisi pour respecter la norme d'employer "on" pour la capture d'un événement.
Enfin, on va créer le bouton dans le template qui déclenchera cette méthode. Il serait intéressant que ce bouton soit contextuel : si l'appareil est allumé, il affichera "Éteindre" et inversement. Pour cela, le plus simple est de créer deux boutons dotés de la directive *ngIf :
appareil.component.html
On peut imaginer qu'à l'intérieur des méthodes du service AppareilService, il y ait des appels API permettant de vraiment allumer ou éteindre les appareils.
L'un des énormes avantages d'utiliser Angular est de pouvoir créer des "single page application" (SPA). Sur le Web, ces applications sont rapides et lisses : il n'y a qu'un seul chargement de page au début, et même si les données mettent parfois du temps à arriver, la sensation pour l'utilisateur est celle d'une application native. Au lieu de charger une nouvelle page à chaque clic ou à chaque changement d'URL, on remplace le contenu ou une partie du contenu de la page : on modifie les components qui y sont affichés, ou le contenu de ces components. On accomplit tout cela avec le "routing", où l'application lit le contenu de l'URL pour afficher le ou les components requis.
L'application des appareils électriques n'a que la view des appareils à afficher pour le moment ; je vous propose de créer un component pour l'authentification (qui restera simulée pour l'instant) et vous créerez un menu permettant de naviguer entre les views.
Tout d'abord, créez le component avec le CLI : ng g c auth
Vous allez également devoir modifier un peu l'organisation actuelle afin d'intégrer plus facilement le routing : vous allez créer un component qui contiendra toute la view actuelle et qui s'appellera AppareilViewComponent :
ng g c appareil-view
Ensuite, coupez tout le contenu de la colonne dans "app.component.html" ,
à partir de la balise h2, et enregistrez-le dans appareil-view.component.html,
et remplacez-le par la nouvelle balise
app.component.html
Il faudra également déménager la logique de cette view pour que tout re-marche : injectez AppareilService, créez l'array appareils, intégrez la logique ngOnInit et déplacez les fonctions onAllumer() et onEteindre() :
appareil-view.component.ts
Vous pouvez faire le ménage dans AppComponent, en retirant tout ce qui n'y sert plus. Créez également une boolean isAuth dans AppareilViewComponent, et déclarez la comme false, car vous allez intégrer un service d'authentification pour la suite. L'application devrai fonctionner.
Créez des routes
Modifier à nouveau le composant racine de l'application AppComponent, et Ajoutez la barre de navigation suivante :
app.component.html
Maintenant, tout est prêt pour créer le routing de l'application. Les routes sont des instructions d'affichage à suivre pour chaque URL, c'est-à-dire quel(s) component(s) il faut afficher à quel(s) endroit(s) pour un URL donné. on déclare les routes dans app.module.ts. Il est possible d'avoir un fichier séparé pour le routing, mais en termes de fonctionnalité, cela ne change rien : c'est juste une question d'organisation du code.
On crée une constante de type Routes (qu'on importe depuis @angular/router ) et on l'enregistre dans l'application en l'ajouttantr à l'array imports de votre AppModule, tout en lui appelant la méthode forRoot()
app.module.ts
Le path correspond à la chaine qui viendra après le "/" dans l'URL : sur votre
serveur local, le premier path ici correspond donc à localhost:4200/appareils .
Il ne reste plus qu'à dire à Angular où vous souhaitez afficher les components
dans le template lorsque l'utilisateur navigue vers la route en question. On
utilise la balise
app.component.html
Lorsque vous changez de route (pour l'instant, en modifiant l'URL directement dans la barre d'adresse du navigateur), la page n'est pas rechargée, mais le contenu sous la barre de navigation change.
Naviguez avec les routerLink
Afin que l'utilisateur puisse naviguer à l'intérieur de votre application, il est nécessaire de créer des liens ou des boutons qui naviguent vers les routes que vous avez créées. Il suffirait de marquer le path de vos routes directement dans l'attribut href, mais la page est rechargée à chaque clic! Du coup, on retire l'attribut href et on le remplace par l'attribut routerLink .
Pour finaliser cette étape, il serait intéressant que la classe active ne s'applique qu'au lien du component réellement actif. Heureusement, Angular fournit un attribut pour cela qui peut être ajouté au lien directement ou à son élément parent :
Naviguez avec le Router
On peut avoir besoin d'authentifier un utilisateur et, si l'authentification fonctionne, de naviguer vers la page que l'utilisateur souhaite voir. Tout d'abord, créez un nouveau fichier auth.service.ts dans le dossier services pour gérer l'authentification ( n'oubliez pas de l'ajouter également dans l'array providers dans AppModule ) :
Dans le component AuthComponent , vous allez simplement créer deux boutons et les méthodes correspondantes pour se connecter et se déconnecter (qui s'afficheront de manière contextuelle : le bouton "se connecter" ne s'affichera que si l'utilisateur est déconnecté et vice versa) :
auth.component.ts
Puisque la méthode signIn() du service retourne une Promise, on peut employer une fonction callback asynchrone avec .then() pour exécuter du code une fois la Promise résolue. Ajoutez simplement les boutons, et tout sera prêt pour intégrer la navigation :
auth.component.html
Le comportement recherché serait qu'une fois l'utilisateur authentifié, l'application navigue automatiquement vers la view des appareils. Pour cela, il faut injecter le Router (importé depuis @angular/router ) pour accéder à la méthode navigate() :
auth.component.ts
La fonction navigate prend comme argument un array d'éléments (ce qui permet de créer des chemins à partir de variables, par exemple)
Paramètres de routes
Tout d'abord, vous allez créer la route dans AppModule : { path: 'appareils/:id', component: SingleAppareilComponent },
L'utilisation des deux-points : avant un fragment de route déclare ce fragment comme étant un paramètre : tous les chemins de type appareils/* seront renvoyés vers SingleAppareilComponent, que l'on crée (ng g c single-appareil):
single-appareil.component.ts
single-appareil.component.html
Pour l'instant, si vous naviguez vers /appareils/nom , peu importe le nom que vous choisissez, vous avez accès à SingleAppareilComponent . Maintenant, vous allez y injecter ActivatedRoute , importé depuis @angular/router , afin de récupérer le fragment id de l'URL :
single-appareil.component.ts
Puis, dans ngOnInit() , vous allez utiliser l'objet snapshot qui contient les paramètres de l'URL et, pour l'instant, attribuer le paramètre id à la variable name :
Ainsi, le fragment que vous tapez dans la barre d'adresse après "appareils/", s'affichera dans le template. Dans AppareilService, ajouter un identifiant unique pour chaque appareil et une méthode qui rendra l'appareil correspondant à un identifiant :
appareil.service.ts
Maintenant, dans SingleAppareilComponent, vous allez récupérer l'identifiant de l'URL et l'utiliser pour récupérer l'appareil correspondant :
single-appareil.component.ts
Puisqu'un fragment d'URL est forcément de type string , et que la méthode getAppareilById() prend un nombre comme argument, il ne faut pas oublier d'utiliser + avant id dans l'appel pour caster la variable comme nombre.
Vous pouvez naviguer manuellement vers /appareils/2 , par exemple, mais cela recharge encore la page, et vous perdez l'état des appareils (si vous en allumez ou éteignez par exemple). Pour finaliser cette fonctionnalité, intégrez l'identifiant unique dans AppareilComponent et dans AppareilViewComponent , puis créez un routerLink pour chaque appareil qui permet d'en regarder le détail :
appareil.component.ts appareil.component.html
@Input() id: number; ...Détail
appareil-view.component.html
Ici, vous utilisez le format array pour routerLink en property binding afin d'accéder à la variable id .
Ça y est! Vous pouvez maintenant accéder à la page Détail pour chaque appareil, et les informations de statut qui s'y trouvent sont automatiquement à jour grâce à l'utilisation du service.
Redirection
commencez par créer un component 404 très simple, four-oh-four.component.ts ng g c four-oh-four
Ensuite, vous allez ajouter la route "directe" vers cette page, ainsi qu'une route "wildcard", qui redirigera toute route inconnue vers la page d'erreur :
app.module.ts
Ainsi, quand vous entrez un chemin dans la barre de navigation qui n'est pas directement pris en charge par votre application, vous êtes redirigé vers /not-found et donc le component 404.
Guards
Pour protéger l'accé à une route, il existe la guard canActivate. Une guard est un service qu'Angular exécutera au moment où l'utilisateur essaye de naviguer vers la route sélectionnée. Ce service implémente l'interface canActivate , et donc doit contenir une méthode du même nom qui prend les arguments ActivatedRouteSnapshot et RouterStateSnapshot (qui lui seront fournis par Angular au moment de l'exécution) et retourne une valeur booléenne, soit de manière synchrone (boolean), soit de manière asynchrone (sous forme de Promise ou d'Observable) : Dans services créer un nouveau fichier auth-guard.service.ts. Puis installer le package rxjs: npm install rxjs-compat --save
auth-guard.service.ts
Ensuite, il faut injecter le service AuthService dans ce nouveau service. Pour injecter un service dans un autre service, il faut que le service dans lequel on injecte un autre ait le décorateur @Injectable, à importer depuis @angular/core.
À l'intérieur de la méthode canActivate() , vous allez vérifier l'état de l'authentification dans AuthService . Si l'utilisateur est authentifié, la méthode renverra true , permettant l'accès à la route protégée. Sinon, vous pourriez retourner false , mais cela empêchera simplement l'accès sans autre fonctionnalité. Il serait intéressant de rediriger l'utilisateur vers la page d'authentification, le poussant à s'identifier :
Pour appliquer cette garde à la route /appareils et à toutes ses routes enfants, il faut l'ajouter dans AppModule. N'oubliez pas d'ajouter AuthGuard à l'array providers , puisqu'il s'agit d'un service :
app.module.ts
Maintenant, si vous essayez d'accéder à /appareils sans être authentifié, vous êtes automatiquement redirigé vers /auth . Si vous cliquez sur "Se connecter", vous pouvez accéder à la liste d'appareils sans problème.
Pour réagir à des événements ou à des données de manière asynchrone ( c'est à dire ne pas devoir attendre qu'une tâche, par exemple un appel HTTP, soit terminée avant de passer à la ligne de code suivante), il y a le système de callback, ou encore les Promise. Avec l'API RxJS, fourni et très intégré dans Angular, la méthode proposée est celle des Observables.
Un Observable est un objet qui émet des informations auxquelles on souhaite réagir. Ces informations peuvent venir d'un champ de texte dans lequel l'utilisateur rentre des données, ou de la progression d'un chargement de fichier, par exemple. Elles peuvent également venir de la communication avec un serveur: le client HTTP, emploie les Observables.
Les Observables sont mis à disposition par RxJS, un package tiers qui est fourni avec Angular. vous trouverez toute la documentation sur le site ReactiveX. À cet Observable, on associe un Observer — un bloc de code qui sera exécuté à chaque fois que l'Observable émet une information. L'Observable émet trois types d'information : des données, une erreur, ou un message complete. Du coup, tout Observer peut avoir 3 fonctions : une pour réagir à chaque type d'information.
Pour créer un cas concret, vous allez créer un Observable dans AppComponent qui enverra un nouveau chiffre toutes les secondes. Vous allez ensuite observer cet Observable et l'afficher dans le DOM. Il faut ajouter deux imports:
app.component.ts
app.component.html
La méthode interval() , qui crée un Observable qui émet un chiffre croissant à intervalles réguliers. La fonction unsubscribe() détruit la souscription et empêche les comportements inattendus liés aux Observables infinis.
Vous allez souscrire à l'Observable et créer trois fonctions : la première va se déclencher à chaque émission de données par l'Observable et va attribuer cette valeur à la variable "secondes" ; la deuxième gèrera toute erreur éventuelle ; et la troisième se déclenchera si l'Observable s'achève :
Subjects
Il existe un type d'Observable qui permet non seulement de réagir à de nouvelles informations, mais également d'en émettre. Imaginez une variable dans un service qui peut être modifié depuis plusieurs components, ET qui fera réagir tous les components qui y sont liés en même temps. Par exemple le tableau d'appareils dans AppareilService, peut être modifié à n'importe quel endroit du code. Cela peut provoquer des bugs avec la manipulation de données. Pour corriger cela, il y a plusieurs étapes : - rendre le tableau des appareils private - créer un Subject dans le service - créer une méthode, quand le service reçoit de nouvelles données, fait émettre ces données par le Subject et appeler cette méthode dans toutes les méthodes qui en ont besoin ; - souscrire à ce Subject depuis AppareilViewComponent pour recevoir les données émises, émettre les premières données, et implémenter OnDestroy pour détruire la souscription.
appareil.service.ts
Quand vous déclarez un Subject, il faut dire quel type de données il gèrera.
Puisque nous n'avons pas créé d'interface pour les appareils, il gèrera des
array de type any[] . N'oubliez pas l'import !
appareil-view.component.ts
L'application refonctionne comme avant, mais avec une différence cruciale de méthodologie : il y a une abstraction entre le service et les components, où les données sont maintenues à jour grâce au Subject. Imaginez qu'on intègre un système qui vérifie périodiquement le statut des appareils. Si les données sont mises à jour par une autre partie de l'application, il faut que l'utilisateur voie ce changement sans avoir à recharger la page. Il va de même dans l'autre sens : un changement au niveau du view doit pouvoir être reflété par le reste de l'application sans rechargement.
Un opérateur est une fonction qui se place entre l'Observable et l'Observer (la Subscription, par exemple), et qui peut filtrer et/ou modifier les données reçues avant même qu'elles n'arrivent à la Subscription. Voici quelques exemples rapides :
map(): modifie les valeurs reçues — peut effectuer des calculs, transformer du texte, créer des objets…
filter(): comme son nom l'indique, filtre les valeurs reçues selon la fonction qu'on lui passe en argument.
throttleTime() : impose un délai minimum entre deux valeurs — par exemple, si un Observable émet 5 valeurs par seconde, mais ce sont uniquement les valeurs reçues toutes les secondes qui vous intéressent, vous pouvez passer throttleTime(1000) comme opérateur.
scan() et reduce(): permettent d'exécuter une fonction qui réunit l'ensemble des valeurs reçues selon une fonction que vous lui passez — par exemple, vous pouvez faire la somme de toutes les valeurs reçues. La différence entre les 2 opérateurs : reduce() vous retourne uniquement la valeur finale, alors que scan() retourne chaque étape du calcul.
En Angular, il y a deux grandes méthodes pour créer des formulaires :
la méthode template : vous créez votre formulaire dans le template, et Angular l'analyse pour comprendre les différents inputs, et pour en mettre à disposition le contenu ;
la méthode réactive : vous créez votre formulaire en TypeScript et dans le template, puis vous en faites la liaison manuellement — cette approche est plus complexe, mais elle permet beaucoup plus de contrôle et une approche dynamique.
N'oubliez pas d'importer FormsModule dans AppModule si ce n'est pas déjà fait !
Créez le formulaire
Pour comprendre et pratiquer la méthode template, vous allez créer un nouveau component EditAppareilComponent qui permettra à l'utilisateur d'enregistrer un nouvel appareil électrique : ng g c edit-appare
edit-appareil.component.html
Angular parcourt votre template et trouve la balise "form" , créant ainsi un objet qui sera utilisable depuis votre code TypeScript.
Il faut signaler à Angular quels inputs correspondront à des controls , c'est-à-dire des champs dont le contenu est à soumettre. Pour cela, il suffit d'ajouter deux attributs aux inputs en question : un attribut name, qui correspondra à la clef de la paire clef-valeur qui sera rendu, et l'attribut ngModel , sans parenthèses ni crochets. Ce deuxième attribut signale à Angular que vous souhaitez enregistrer ce contrôle :
Il faut préparer la soumission en déclarant le bouton de type submit, et en ajoutant du code dans la balise form. En ajoutant l'attribut (ngSubmit), vous recevez cette soumission et exécutez la méthode onSubmit(). L'attribut #f est ce qu'on appelle une référence locale. On y attribue la valeur ngForm qui est envoyé à onSubmit.
edit-appareil.component.ts
Pour avoir accès au formulaire, créez une nouvelle route dans AppModule et un routerLink correspondant dans la barre de menu :
{ path: 'edit', canActivate: [AuthGuard], component: EditAppareilComponent },
app.component.html
Validez les données
Le nom de l'appareil est un champ obligatoire. Ajoutez simplement la directive:
Le champ est obligatoire, mais le formulaire peut encore être soumis, car vous n'avez pas encore intégré l'état de validation du formulaire. Pour accomplir cela, vous allez lier la propriété disabled du bouton à la propriété invalid du formulaire, qui est mise à disposition par Angular :
Vous employez la référence locale f pour avoir accès à l'objet NgForm, et donc à sa propriété invalid, désactivant le bouton si elle retourne true. Pour déclarer une réponse par défaut, vous allez créer une variable TypeScript et la lier à la propriété ngModel du contrôle :
Exploitez les données
Créer la méthode de création dans AppareilService
appareil.service.ts
Il ne reste plus qu'à intégrer cette fonction dans EditAppareilComponent . Il serait intéressant qu'une fois l'appareil créé, l'utilisateur soit redirigé vers la liste des appareils. N'oubliez pas d'importer et d'injecter le router pour cela, ainsi qu' AppareilService :
edit-appareil.component.ts
À la différence de la méthode template où Angular crée l'objet du formulaire, pour la méthode réactive, vous devez le créer vous-même et le relier à votre template. Même si cela a l'air plus complexe, cela vous permet de gérer votre formulaire en détail, notamment avec la création programmatique de contrôles (permettant, par exemple, à l'utilisateur d'ajouter des champs). Pour illustrer la méthode réactive, vous allez permettre aux utilisateurs de créer un profil.
Commencez par le modèle User ; créez un nouveau dossier models , et dedans un fichier user.model.ts.
On utilise ici un raccourcis typeScript; en ajoutant le terme public devant les paramètres du constructeur, ts va créer des proprietés avec ces noms dans user. Ce modèle pourra donc être utilisé dans le reste de l'application en l'important dans les components où vous en avez besoin. Cette syntaxe de constructeur permet l'utilisation du mot-clé new , et les arguments passés seront attribués à des variables qui portent les noms choisis.
Ensuite, créez un UserService simple qui stockera la liste des objets User et qui comportera une méthode permettant d'ajouter un utilisateur à la liste :
user.service.ts
Ce service contient un array privé d'objets de type personnalisé User et un Subject pour émettre cet array. La méthode emitUsers() déclenche ce Subject et la méthode addUser() ajoute un objet User à l'array, puis déclenche le Subject. N'oubliez pas d'ajouter ce nouveau service à l'array providers dans AppModule !
L'étape suivante est de créer UserListComponent — pour simplifier on ne va pas créer un component pour les utilisateurs individuels: ng g c user-list
user-list.component.ts
Ce component très simple souscrit au Subject dans UserService et le déclenche pour en récupérer les informations et les rendre disponibles au template :
user-list.component.html
Afin de pouvoir visualiser ce nouveau component, ajoutez une route users dans AppModule, et créez un routerLink. Ajoutez également un objet User codé en dur dans le service pour voir les résultats :
app.module.ts app.component.html
user.service.ts
Dernière étape: il faut ajouter ReactiveFormsModule à l'array imports de votre AppModule (à importer depuis @angular/forms).
Maintenant que tout est prêt, vous allez créer NewUserComponent qui contiendra votre formulaire réactif.
Construisez un formulaire avec FormBuilder
Dans la méthode template, l'objet formulaire mis à disposition par Angular était de type NgForm , mais ce n'est pas le cas pour les formulaires réactifs. Un formulaire réactif est de type FormGroup , et il regroupe plusieurs FormControl (tous les deux importés depuis @angular/forms ). Vous commencez d'abord, donc, par créer l'objet dans votre nouveau component NewUserComponent: ng g c new-user
Ensuite, vous allez créer une méthode qui sera appelée dans le constructeur pour la population de cet objet, et vous allez également injecter FormBuilder importé depuis @angular/forms.
FormBuilder est une classe qui vous met à disposition des méthodes facilitant la création d'objet FormGroup. Vous allez maintenant utiliser la méthode group à l'intérieur de votre méthode initForm() pour commencer à créer le formulaire :
new-user.component.ts
La méthode group prend comme argument un objet où les clés correspondent aux noms des contrôles souhaités et les valeurs correspondent aux valeurs par défaut de ces champs. Il faut maintenant créer le template du formulaire et lier ce template à l'objet userForm que vous venez de créer :
new-user.component.html
Sur la balise "form" , vous utilisez le property binding pour lier l'objet userForm à l'attribut formGroup du formulaire, créant la liaison pour Angular entre le template et le TypeScript.
Également dans la balise "form" , vous avez toujours une méthode onSubmitForm() liée à ngSubmit, mais vous n'avez plus besoin de passer le formulaire comme argument puisque vous y avez déjà accès par l'objet userForm que vous avez créé.
Sur chaque "input" qui correspond à un control du formulaire, vous ajoutez l'attribut formControlName où vous passez un string correspondant au nom du control dans l'objet TypeScript.
Le bouton de type submit déclenche l'événement ngSubmit , déclenchant ainsi la méthode onSubmitForm() , que vous allez créer dans votre TypeScript.
Pour tout mettre ensemble, injectez UserService et Router (sans oublier de les importer) dans le constructeur du component, et créez la méthode onSubmitForm():
new-user.component.ts
La méthode onSubmitForm() récupère la value du formulaire, et crée un nouvel objet User (à importer en haut) à partir de la valeur des controls du formulaire. Ensuite, elle ajoute le nouvel utilisateur au service et navigue vers /users pour en montrer le résultat.
Ajouter un lien dans UserListComponent qui permet d'accéder à NewUserComponent, et de créer la route correspondante new-user dans AppModule
app.module.ts user-list.component.html
Validators
Comme pour la méthode template, il existe un outil pour la validation de données dans la méthode réactive: les Validators. Pour ajouter la validation, vous allez modifier légèrement votre exécution de FormBuilder.group :
Plutôt qu'un string simple, vous passez un array à chaque control , avec comme premier élément la valeur par défaut souhaitée, et comme deuxième élément le ou les Validators (dans un array s'il y en a plusieurs) souhaités. Il faut également importer Validators depuis @angular/forms .
En liant la validité de userForm à la propriété disabled du bouton submit , vous intégrez la validation de données :
Ajoutez dynamiquement des FormControl
Pour l'instant, vous n'avez pas encore laissé la possibilité à l'utilisateur d'ajouter ses hobbies. Il serait intéressant de lui laisser la possibilité d'en ajouter autant qu'il veut, et pour cela, vous allez utiliser un FormArray . Un FormArray est un array de plusieurs FormControl , et permet, par exemple, d'ajouter des nouveaux controls à un formulaire. Vous allez utiliser cette méthode pour permettre à l'utilisateur d'ajouter ses hobbies.
Modifiez d'abord initForm() pour ajouter un FormArray vide qui s'appellera hobbies avec la méthode array :
Modifiez ensuite onSubmitForm() pour récupérer les valeurs, si elles existent (sinon, retournez un array vide) :
new-user.component.ts
Afin d'avoir accès aux controls à l'intérieur de l'array, pour des raisons de typage strict liées à TypeScript, il faut créer une méthode qui retourne hobbies par la méthode get() sous forme de FormArray ( FormArray s'importe depuis @angular/forms ) :
getHobbies(): FormArray { return this.userForm.get('hobbies') as FormArray; }
Ensuite, vous allez créer la méthode qui permet d'ajouter un FormControl à hobbies , permettant ainsi à l'utilisateur d'en ajouter autant qu'il veut. Vous allez également rendre le nouveau champ requis, afin de ne pas avoir un array de hobbies avec des string vides :
Cette méthode crée un control avec la méthode FormBuilder. control() , et l'ajoute au FormArray rendu disponible par la méthode getHobbies(). Enfin, il faut ajouter une section au template qui permet d'ajouter des hobbies en ajoutant des "input" :
new-user.component.html
Analysez cette "div" :
à la "div" qui englobe toute la partie hobbies , vous ajoutez l'attribut formArrayName , qui correspond au nom choisi dans votre TypeScript ;
la "div" de class form-group est ensuite répété pour chaque FormControl dans le FormArray (retourné par getHobbies() , initialement vide, en notant l'index afin de créer un nom unique pour chaque FormControl ;
dans cette "div" , vous avec une "input" qui prendra comme formControlName l'index du FormControl ;
enfin, vous avez le bouton (de type button pour l'empêcher d'essayer de soumettre le formulaire) qui déclenche onAddHobby() , méthode qui, pour rappel, crée un nouveau FormControl (affichant une nouvelle instance de la "div" de class form-group , et donc créant une nouvelle "input" )
Pour l'instant, la capacité d'enregistrement et de gestion de ces données a été limitée au service et donc tout est systématiquement remis à zéro à chaque rechargement de l'app. Dans les chapitres suivants, vous allez apprendre à interagir avec un serveur (et puis, plus précisément, avec un backend Firebase) afin de rendre les données permanentes et que votre application soit totalement dynamique.
Dans une application Angular, vous aurez très souvent besoin de faire des appels à un backend ou à un autre serveur — pour enregistrer ou charger des données, par exemple, ou pour effectuer des calculs ou des modifications de données que vous ne souhaitez pas faire faire par le frontend. Angular met à disposition un service appelé HttpClient qui permet de créer et d'exécuter des appels HTTP (fait par AJAX - Asynchronous JavaScript and XML) et de réagir aux informations retournées par le serveur.
Télécharger un backend php rest sqlite.
https://github.com/simplonco/php-rest-sqlite/blob/master/index.php
Créer un alias sur le dossier dans wamp http://localhost:81/backend/
Access-Control-Allow-Origin
Modifier index.php ainsi:
Envoyez vers le backend
Pour avoir accès au service HttpClient, ajouter HttpClientModule (importé depuis @angular/common/http), à votre AppModule dans le tableau imports :
Injecter HttpClient dans AppareilService (avec le décorateur @Injectable())
appareil.service.ts
Vous allez d'abord créer une méthode qui va enregistrer l'array appareils dans la base de données.
la méthode post() , qui permet de lancer un appel POST, prend comme premier argument l'URL visée, et comme deuxième argument le corps de l'appel, c'est-à-dire ce qu'il faut envoyer à l'URL ;
la méthode post retourne un Observable — elle ne fait pas d'appel à elle toute seule. C'est en y souscrivant que l'appel est lancé ;
dans la méthode subscribe() , vous prévoyez le cas où tout fonctionne et le cas où le serveur vous renverrait une erreur.
Créez un bouton dans AppareilViewComponent qui déclenche cette sauvegarde.
appareil-view.component.html appareil-view.component.ts
Enregistrez le tout, et cliquez sur le bouton que vous venez de crée: vous devez avoir votre message de réussite qui apparait dans la console.
Recevez depuis le backend
Afin de demander la liste des appareils (stocké au endpoint /appareils ), vous allez créer une nouvelle méthode qui emploie la méthode get dans AppareilService
la méthode get retourne un Observable, mais puisqu'ici, vous allez recevoir des
données, TypeScript a besoin de savoir de quel type elles seront ( l'objet
retourné est d'office considéré comme étant un Object). Vous devez donc, dans ce
cas précis, ajouter
Vous pouvez maintenant vider l'array appareils du service et intégrer un bouton au component permettant de déclencher la méthode getAppareilsFromServer() :
appareil-view.component.html__ _appareil-view.component.ts
Maintenant, vous pouvez ajouter de nouveaux appareils, en modifier l'état et les sauvegarder, puis récupérer la liste sauvegardée. Il serait également possible de rendre automatique le chargement et l'enregistrement des appareils (par exemple en appelant la méthode getAppareilsFromServer() dans ngOnInit() , et saveAppareilsToServer() après chaque modification), mais j'ai souhaité vous laisser la possibilité de les exécuter manuellement afin de voir le résultat de manière plus concrète.
Vous allez créer une application qui recense les livres que vous avez chez vous, dans votre bibliothèque. Vous pourrez ajouter une photo de chaque livre. L'utilisateur devra être authentifié pour utiliser l'application.
L'application nécessite l'authentification. Il faudra donc un component pour la création d'un nouvel utilisateur, et un autre pour s'authentifier, avec un service gérant les interactions avec le backend.
Les livres pourront être consultés sous forme d'une liste complète, puis individuellement. Il faut également pouvoir ajouter et supprimer des livres. Il faudra donc un component pour la liste complète, un autre pour la vue individuelle et un dernier comportant un formulaire pour la création/modification. Il faudra un service pour gérer toutes les fonctionnalités liées à ces components, y compris les interactions avec le serveur.
Vous créerez également un component séparé pour la barre de navigation afin d'y intégrer une logique séparée.
Pour les modèles de données, il y aura un modèle pour les livres, comportant simplement le titre, le nom de l'auteur et la photo, qui sera facultative.
Il faudra également ajouter du routing à cette application, permettant l'accès aux différentes parties, avec une guard pour toutes les routes sauf pour l'authentification, empêchant les utilisateurs non authentifiés d'accéder à la bibliothèque.
Structurez l'application
Pour cette application, je vous conseille d'utiliser le CLI pour la création des components. L'arborescence sera la suivante :
angular.json, section "architect/build/options", modifiez l'array: "styles": ["node_modules/bootstrap/dist/css/bootstrap.css",
Les services ainsi créés ne sont pas automatiquement mis dans l'array providers d'AppModule, donc ajoutez-les. Pendant que vous travaillez sur AppModule, ajoutez également FormsModule, ReactiveFormsModule et HttpClientModule. Intégrez dès maintenant le routing sans guard afin de pouvoir accéder partout :
Générez également un dossier appelé models et créez-y le fichier book.model.ts
Enfin, préparez HeaderComponent avec un menu de navigation, avec les routerLink et AppComponent qui l'intègre avec le router-outlet :
header.component.html
app.component.html
Intégrez Firebase à votre application avec npm install firebase --save, ou bien utiliser le simulateur.
Pour cette application, vous allez créer un nouveau projet sur Firebase. Une fois l'application créée, la console Firebase vous propose le choix suivant (sous la rubrique Overview) : - Ajouter Firebase à votre application iOS - Ajouter Firebase à votre application Android - Ajouter Firebase à votre application Web Choisissez "Ajouter Firebase à votre application Web" et copiez-collez la configuration dans le constructeur de votre AppComponent. Importez Firebase: import * as firebase from 'firebase'; Ou le simulateur : import { firebase } from './api/firebase';
Votre application Angular est maintenant liée à votre projet Firebase, et vous pourrez maintenant intégrer tous les services dont vous aurez besoin.
Authentification
Votre application utilisera l'authentification par adresse mail et mot de passe proposée par Firebase. Pour cela, il faut d'abord l'activer dans la console Firebase :
Authentifiez et gérez les utilisateurs à partir de plusieurs fournisseurs sans code coté serveur s [ Configurer un mode de connexion ]
Adresse e-mail/Mot de passe Activer =O [ANNULER] [ENREGISTRER]
Dans AuthService , vous allez créer trois méthodes : - une méthode permettant de créer un nouvel utilisateur ; - une méthode permettant de connecter un utilisateur existant ; - une méthode permettant la déconnexion de l'utilisateur.
Puisque les opérations de création, de connexion et de déconnexion sont asynchrones, c'est-à-dire qu'elles n'ont pas un résultat instantané, les méthodes que vous allez créer pour les gérer retourneront des Promise, ce qui permettra également de gérer les situations d'erreur.
Importez Firebase dans AuthService : import * as firebase from 'firebase'; Ou le simulateur : import { firebase } from './api/firebase';
Ensuite, créez la méthode createNewUser() pour créer un nouvel utilisateur, qui prendra comme argument une adresse mail et un mot de passe, et qui retournera une Promise qui résoudra si la création réussit, et sera rejetée avec le message d'erreur si elle ne réussit pas :
Créez également signInUser() , méthode très similaire, qui s'occupera de connecter un utilisateur déjà existant :
Créez une méthode simple signOutUser() :
Ainsi, vous avez les trois fonctions dont vous avez besoin pour intégrer l'authentification dans l'application !
Vous pouvez ainsi créer SignupComponent et SigninComponent , intégrer l'authentification dans HeaderComponent afin de montrer les bons liens, et implémenter AuthGuard pour protéger la route "/books" et toutes ses sous-routes.
Commencez par SignupComponent afin de pouvoir enregistrer un utilisateur :
signup.component.ts
le formulaire est géré selon la méthode réactive. Les deux champs, email et password, sont requis — le champ email utilise Validators.email pour obliger un string sous format d'adresse email; le champ password emploie Validators.pattern pour obliger au moins 6 caractères alphanumériques, ce qui correspond au minimum requis par Firebase ;
pour la soumission du formulaire, les valeurs rentrées par l'utilisateur sont& envoyées à la méthode createNewUser()
si la création fonctionne, vous redirigez l'utilisateur vers /books ;
si elle ne fonctionne pas, vous affichez le message d'erreur.
Ci-dessous, vous trouverez le template correspondant :
signup.component.html
Il s'agit d'un formulaire selon la méthode réactive comme vous l'avez vu dans le chapitre correspondant. Il y a aussi le paragraphe contenant l'éventuel message d'erreur rendu par Firebase.
Vous pouvez créer un template presque identique pour SignInComponent, pour la connexion d'un utilisateur déjà existant. Il vous suffit de renommer signupForm en signinForm et d'appeler la méthode signInUser() plutôt que createNewUser().
Ensuite, modifier HeaderComponent pour afficher de manière contextuelle les liens de connexion, de création d'utilisateur et de déconnexion :
header.component.ts
header.component.html
Ici, vous utilisez onAuthStateChanged() , qui permet d'observer l'état de l'authentification de l'utilisateur : à chaque changement d'état, la fonction que vous passez en argument est exécutée. Si l'utilisateur est bien authentifié, onAuthStateChanged() reçoit l'objet de type firebase. User correspondant à l'utilisateur. Vous pouvez ainsi baser la valeur de la variable locale isAuth selon l'état d'authentification de l'utilisateur, et afficher les liens correspondant à cet état.
Il ne vous reste plus qu'à créer AuthGuardService et l'appliquer aux routes concernées. Puisque la vérification de l'authentification est asynchrone, votre service retournera une Promise : Pour utiliser l'observable, n'oubliez pas d'installer le package rxjs: npm install rxjs-compat --save
auth-guard.service.ts
app.module.ts
Ainsi, votre application comporte un système d'authentification complet, permettant l'inscription et la connexion/déconnexion des utilisateurs, et qui protège les routes concernées. On peut ajouter des fonctionnalités à votre application en sachant que les accès à la base de données et au stockage, qui nécessitent l'authentification, fonctionneront correctement.
Base de données
Dans ce chapitre, vous allez créer les fonctionnalités de l'application : la création, la visualisation et la suppression des livres, le tout lié directement à la base de données Firebase.
Pour créer BooksService vous aurez un array local books et un Subject pour l'émettre. Vus aurez des méthodes : - pour enregistrer la liste des livres sur le serveur, - pour récupérer la liste des livres depuis le serveur, - pour récupérer un seul livre, - pour créer un nouveau livre, - pour supprimer un livre existant.
Pour la 1ère étape, rien de nouveau (sans oublier d'importer Book et Subject) :
Ensuite, vous allez utiliser une méthode mise à disposition par Firebase pour enregistrer la liste sur un node de la base de données — la méthode set() :
La méthode ref() retourne une référence au node demandé de la base de données, et set() fonctionne plus ou moins comme put() pour le HTTP : il écrit et remplace les données au node donné.
Maintenant que vous pouvez enregistrer la liste, vous allez créer les méthodes pour récupérer la liste entière des livres et pour récupérer un seul livre, en employant les deux fonctions proposées par Firebase :
books.service.ts
Pour getBooks() , vous utilisez la méthode on() . Le premier argument 'value' demande à Firebase d'exécuter le callback à chaque modification de valeur enregistrée au endpoint choisi : cela veut dire que si vous modifiez quelque chose depuis un appareil, la liste sera automatiquement mise à jour sur tous les appareils connectés. Le deuxième argument est la fonction callback, qui reçoit ici une DataSnapshot : un objet correspondant au node demandé, comportant plusieurs membres et méthodes (il faut importer DataSnapshot depuis firebase. database.DataSnapshot). La méthode qui vous intéresse ici est val() , qui retourne la valeur des données, tout simplement. Votre callback prend également en compte le cas où le serveur ne retourne rien pour éviter les bugs potentiels. Ajoutez un constructor au service pour appeler getBooks() au démarrage.
La fonction getSingleBook() récupère un livre selon son id, qui est simplement ici son index dans l'array enregistré. Vous utilisez once(), qui ne fait qu'une seule requête de données. Du coup, elle ne prend pas une fonction callback en argument mais retourne une Promise, permettant l'utilisation de . then() pour retourner les données reçues.
Il ne reste plus qu'à créer les méthodes pour la création d'un nouveau livre et la suppression d'un livre existant :
Ensuite, vous allez créer BookListComponent , qui : - souscrit au Subject du service et déclenche sa première émission ; - affiche la liste des livres, où chaque livre peut être cliqué pour en voir la page SingleBookComponent ; - permet de supprimer chaque livre en utilisant removeBook() ; - permet de naviguer vers BookFormComponent pour la création d'un nouveau livre.
book-list.component.ts
book-list.component.html
book-list/single-book/single-book.component.ts
Le component récupère le livre demandé par son id grâce à getSingleBook() , et l'affiche dans le template suivant :
book-list/single-book/single-book.component.html
Il ne reste plus qu'à créer BookFormComponen , qui comprend un formulaire selon la méthode réactive et qui enregistre les données reçues grâce à createNewBook()
book-list/book-form/book-form.component.ts
book-list/book-form/book-form.component.html
Et ça y est, votre application fonctionn ! Elle enregistre et lit votre liste de livres sur votre backend Firebase, rendant ainsi son fonctionnement totalement dynamique !
Storage
Dans ce dernier chapitre, vous allez apprendre à utiliser l'API Firebase Storage afin de permettre à l'utilisateur d'ajouter une photo du livre, de l'afficher dans SingleBookComponent et de la supprimer si on supprime le livre, afin de ne pas laisser des photos inutilisées sur le serveur.
Tout d'abord, vous allez ajouter une méthode dans BooksService qui permet d'uploader une photo :
Maintenant que le service est prêt, vous allez ajouter les fonctionnalités à BookFormComponent. Commencez par ajouter quelques membres supplémentaires au :
book-form.component.ts
Ensuite, créez la méthode qui déclenchera uploadFile() et qui en récupérera l'URL retourné :
Vous utiliserez fileIsUploading pour désactiver le bouton submit du template pendant le chargement du fichier afin d'éviter toute erreur — une fois l'upload terminé, le component enregistre l'URL retournée dans fileUrl et modifie l'état du component pour dire que le chargement est terminé. Il faut modifier légèrement onSaveBook() pour prendre en compte l'URL de la photo si elle existe:
Vous allez créer une méthode qui permettra de lier le (que vous créerez par la suite) à la méthode onUploadFile() :
L'événement est envoyé à cette méthode depuis cette nouvelle section du template
Dès que l'utilisateur choisit un fichier, l'événement est déclenché et le fichier est uploadé. Le texte "Fichier chargé ! " est affiché lorsque fileUploaded est true , et le bouton est désactivé quand le formulaire n'est pas valable ou quand fileIsUploading est true .
Il ne reste plus qu'à afficher l'image, si elle existe, dans SingleBookComponent
single-book.component.html
Il faut également prendre en compte que si un livre est supprimé, il faut également en supprimer la photo. La nouvelle méthode removeBook() est la suivante :
books.service.ts
Puisqu'il faut une référence pour supprimer un fichier avec la méthode delete(), vous passez l'URL du fichier à refFromUrl() pour en récupérer la référence.
Déployez votre application
Ça y est ! L'application est prête à être déployée. Si votre serveur de dév tourne encore, arrêtez-le et exécutez la commande suivante : ng build --prod Vous utilisez le CLI pour générer le package final de production de votre appli dans le dossier dist. Le dossier dist contient tous les fichiers à charger sur votre serveur de déploiement pour votre application.
réf: Développez des applications Web avec Angular
https://openclassrooms.com/fr/courses/4668271-developpez-des-applications-web-avec-angular
Nomenclature
Les noms des fichiers html: kebal-case ou all-lower-case-dash (user-my-wishlist) Les noms des variables css: kebal-case ou all-lower-case-dash (nav-bar-title) Le css adopte cette règle: les attributs et les valeurs css sont de cette forme. Les noms des classes Typescript: UpperCamelCase (la 1ère lettre du mot en maj.). pour avoir le nom des classes en UpperCamelCase, il faut séparé les mots par "-" ng g component user-wish-list => UserWishList dans "user-wish-list.component.ts" Les noms des attributs dans une classe ou model: lowerCamelCase: le 1er mot est en minuscule, les autres mots du nom commencent par une majuscule. Les constantes: UpperCamelCase et statique: MAX_SEARCH_DISPLAYED_ITEMS = 4; Les noms des méthodes ou fonction: lowerCamelCase isCategoryItemActive
Test du responsive
Outils de vérification du responsive:
https://bluetree.ai/screenfly/#u=http%3A//localhost%3A4200/&w=360&h=640&a=39
http://ami.responsivedesign.is/
#
http://www.isresponsive.com/Tester?site=http://localhost:4200/
Breakpoint cibles: nous devons avoir deux versions pour ne pas alourdir le code avec des if et des cas particuliers : - une version pour les desktop, les iPads & les tablettes. - une version mobile.
Règles générales
Les noms des classes, variables et attributs doivent être significatifs: le nom suffit pour connaitre le rôle et l'emplacement.
Produire plutot des petites fonctions, plutot qu'une seule qui fait tout. Cela permet de simplifier la compréhension du programme, et maximise la réutilisation du code de part sa granularité.
Il faut mettre des commentaires dans le code typescript avant chaque méthode, dans le code html et dans le code css. Les commentaires sont très importants pour expliquer ce que fait une fonction sans avoir besoin de comprendre le code.
Les urls sont déclarés comme constantes (statiques) une et une seule fois.
Chaque string utilisée dans le html, doit obligatoirement passée par "translate" même si elle a la même valeur dans toutes les langues, pour éviter le retard en cas de retours de vérifications.
Chaque composant html doit avoir sa propre mise en forme css pour: - éviter les cas comme changer la mise en forme d'un élément entraine le changement non voulu d'un autre. - automatiser la mise en forme. - séparation entre développement et mise en forme: si on donne le fichier css à un intégrateur web ou designer, il peut modifier la mise en forme sans risques pour l'application et les parties déjà stabilisées.
Privilégier le module au component si le module est réutilisable par d'autres applications: automatisation via npm
Le Typescript est un langage orienté objet: respecter les règles de la POO - séparation du contexte: séparation des couches: UI séparé des services et des providers. - single responsability principle: une classe doit effectuée un seul rôle. Par exemple une classe rest-services qui effectue le chargement des famous, recipes et drinks doit être décomposée en 3 classes. - Open Closed Principle : "you should be able to extend the behavior of your system without having to modify it". Nous devons écrire un code qui ne doit pas être changé chaque fois que les conditions changent (fichier constante, settings séparé du code).
Utilisez la bibliothèque Redux ou des wrappers spécifiques au framework comme ng2-redux. Permet une gestion d'état prévisible.
Différence entre Undefined et Null en Javascript: Undefined signifie qu’une variable a été déclarée mais qu’aucune valeur n’a encore été affectée. Tandis que, null est une valeur d’affectation. Il peut être affecté à une variable en tant que représentation sans valeur. En plus, undefined et null sont deux types distincts: undefined est un type indéfini tandis que null est un objet. Les variables non attribuées sont initialisées par JavaScript avec la valeur par défaut « undefined ». JavaScript ne définit jamais une valeur sur null. Cela doit être fait par un programmeur.
réf: Les règles de développement Angular
https://fr.slideshare.net/helabenkhalfallah/les-rgles-de-dveloppement-angular
réf: Angular2 guidelines
https://github.com/rangle/angular2-guidelines
réf: Différence entre Undefined et Null en Javascript
https://waytolearnx.com/2019/04/difference-entre-undefined-et-null-en-javascript.html
Typescript, angular-cli, RxJs, Material, flex-layout… beaucoup de choses à apprendre avant d’être performants ! D’une façon générale apprendre tous les concepts du framework et être à l’aise avec prend beaucoup de temps. Une fois que vous serez bon sur le framework et que vous n’avez plus de points de blocage, vous allez vraiment exploser votre productivité tout en maintenant une qualité de code et une maintenabilité optimale grâce à l’architecture Angular.
En faisant quelques recherches rapides nous obtenons ces résultats, pour avoir un ordre de grandeur :
Linkedin Indeed.fr
Angular 3950 3700 React 2319 2395 Vue 775 640
réf:
https://blog.dyma.fr/quel-framework-choisir-en-2020-angular-vue-js-ou-react/
Qualifié de “bundler”, WebPack vous permettra de faire bien des choses: utiliser un serveur local, utiliser le Live Reload, mais aussi et surtout compiler tous vos fichiers pour les regrouper en un seul. Pratique pour bien des raisons, notamment pour la performance de votre app !
Afin de préparer votre projet à utiliser les dépendances NPM, ouvrez votre terminal, rendez-vous à la racine de votre projet et tapez la commande suivante
npm init -y
Ensuite, vous allez devoir créez 2 dossiers: "public" et "src", et un fichier webpack.config. js à la racine. "public" contenant deux fichiers: index. html, bundle.js et src contenant 1 fichier index.js). Le dossier src/ nous permettra de stocker la logique de notre application tandis que le dossier public nous permettra de stocker tous nos fichiers minifiés. Il ne manque plus qu’un dossier assets qui comprendra lui même 4 dossiers : fonts, icons, images et stylesheets.
Installation de Webpack
Pour installer webpack sur le projet et de manière globale, tapez: npm install --save-dev webpack@latest webpack-dev-server@latest npm install -g webpack@latest
Les flags --save-dev (ou raccourci avec -D) indiquent que Webpack correspond à une dépendance de développement, que l'on a besoin de ces fichiers durant cette phase. Vous pourrez le vérifier dans votre fichier package. json où il sera affiché sous la section dev dependencies. Un dossier node_modules va apparaître dans votre dossier : il s’agit des dépendances dont Webpack va avoir besoin pour fonctionner. Ouvrez webpack.config.js et ajoutez les lignes suivantes :
_webpack.config.js _
Nous créons ici une constante appelée webpack, qui requiert le module webpack. C’est la base pour que tout fonctionne. Puis nous créons la variable path qui va nous permettre d’indiquer les bons chemins vers nos fichiers.
Ensuite, il va falloir indiquer un point d’entrée (notre fichier index. js), c’est-à-dire le fichier qui sera lu et un point de sortie, à savoir le fichier qui sera compilé (bundle.js). Ensuite, nous allons exporter cette configuration:
_webpack.config.js _
Maintenant, n’oubliez pas de lier votre fichier bundle. js à votre fichier index. html en tapant la ligne de code qui suit, juste avant la fermeture de votre balise "body" :
index.html index.js
document.write("Je débute avec Webpack !");
Maintenant, tapez simplement webpack dans votre terminal et laissez la magie opérer. Profitez-en pour ouvrir votre index.html avec le navigateur pour voir le contenu de votre page !
Un peu d’automatisation
Un flag existe pour éviter de taper webpack à chaque changement : --watch ou -w. Tapez webpack --watch . Dans votre fichier package.json modifier
Maintenant, lorsque vous voudrez lancer la commande watch depuis votre terminal, tapez npm run watch.
Écrire de l’ES6 ? Le convertir en ES5 ? C’est possible !
Vous avez sûrement déjà entendu parler de l’ES6 (ECMAScript 6), une version récente du langage Javascript. Vous devez donc savoir qu’il n’est pas possible pour tous les navigateurs (les plus anciens) d'interpréter l’ES6. Webpack va nous permettre, en collaboration avec Babel, de compiler l’ES6 en ES5, ce qui rendra votre syntaxe récente (par exemple let et autres fonctions fléchées) parfaitement utilisable. Pour cela, nous allons installer les dépendances Babel Loader et Babel Core :
npm install --save-dev babel-loader babel-core
Ajoutez ensuite le code suivant à votre fichier de configuration Webpack, après l’objet output :
_webpack.config.js _
Maintenant, nous allons devoir indiquer à Babel qu’il doit utiliser le preset (pré-réglage) ES2015. Pour ce faire, entrez la commande suivante :
npm install --save-dev babel-preset-env
Créez un fichier .babelrc à la racine de votre projet et ajoutez-y le code :
Il va rendre votre ES6 compatible avec les 2 dernières de tous les navigateurs et les versions de Safari supérieures ou égales à la 7. Babel utilisant Browserlist, je vous invite à vous rendre sur la page GitHub du projet si vous souhaitez en apprendre plus sur cette partie de la configuration.
Allez dans votre fichier index.js et tapez de l’ES6. let a = "J'apprends Webpack !";
Ensuite, lancez Webpack avec npm run watch. Ouvrez votre fichier bundle. js et rendez-vous tout en bas. Si tout à bien fonctionné, vous devriez avoir var a = à la place de let, signe que Webpack a bien compilé l’ES6 en ES5 !
Compiler du SCSS en CSS ? C’est possible aussi !
Pour cela, vous allez avoir besoin d’installer de nouvelles dépendances :
npm i --save-dev sass-loader node-sass css-loader style-loader autoprefixer postcss-loader
Tapez le code suivant dans webpack.config.js après le loader babel-loader :
Votre fichier webpack.config.js doit désormais ressembler à ça :
_webpack.config.js _
Créez un fichier styles.scss dans le dossier assets/stylesheets pour tester ça. Il s’agit de CSS évolué, permettant notamment d’utiliser des variables.
$midnight-blue : #2c3e50; body { background-color: $midnight-blue; } Dans index.js, nous allons maintenant require ce fichier.
require("../assets/stylesheets/styles.scss");
Avant de vérifier que tout fonctionne, j’attire votre attention sur PostCSS et autoprefixer : ce sont deux outils qui vont vous permettre de préfixer automatiquement votre code CSS pour tous les navigateurs. Il ne vous sera donc plus nécessaire de penser écrire des propriétés CSS spécifiques pour -webkit- (WebKit, Safari, Opera), -moz- (Firefox), -o- (anciennes versions d'Opera) et -ms- (Microsoft). Tout sera fait automatiquement ! Pour en profiter, créez un fichier postcss.config.js à la racine du projet :
postcss.config.js
Cela ne fonctionnera que dans votre environnement de développement. Tant que le tout n’est pas extrait dans un fichier CSS, vos utilisateurs ne pourront pas profiter de vos belles interfaces. Nous allons donc faire en sorte que tout code écrit dans un fichier SCSS soit compilé en CSS dans un fichier adéquat.
Pour cela, nous allons ajouter de nouvelles dépendances :
npm install --save-dev extract-text-webpack-plugin
Ajoutez la ligne suivante sous la ligne const path de votre webpack.config.js
const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");
Ensuite, modifier ainsi :
new ExtractTextWebpackPlugin va initialiser le plugin. Lancez npm run watch : si un fichier "styles.css" a bien été généré dans votre dossier public avec du CSS, c’est que tout est bon ! N’oubliez pas de linker votre fichier CSS à votre HTML.
Installer un serveur de développement avec Hot Module Reload HMR
Nous allons maintenant mettre en place un serveur de développement avec Hot Module Reload, qui rechargera automatiquement vos pages en fonction des modifications que vous apporterez à vos fichiers et qui en plus fera office de serveur local.
npm install webpack-dev-server --save-dev
Après l’array de votre objet plugins dans votre fichier webpack.config.js écrire le code suivant :
Pour lancer votre serveur remplacez l’objet scripts de votre package.json par :
Il vous suffit de taper npm run start pour lancer votre serveur et non plus npm run watch comme nous le faisions jusqu’alors. Si tout se passe bien, votre navigateur va alors s’ouvrir avec un onglet contenant votre site. Et là, vous allez sûrement remarquer que votre page ne s’actualise pas automatiquement au gré de vos changements. Du coup, nous allons installer la dépendance css hot loader :
npm install css-hot-loader --save-dev
Puis nous allons modifier webpack.config.js :
Vous pouvez désormais relancer Webpack avec npm run start et faire quelques changements dans votre fichier SCSS pour les voir opérer en live !
Minifier les fichiers Javascript
La minification, vous connaissez? En gros, il s’agit de réduire la taille de vos fichiers de manière à accélérer leur chargement en retirant les caractères et espaces superflus. Le plugin UglifyJS va nous permettre d’atteindre ce but.
npm install uglifyjs-webpack-plugin --save-dev
_webpack.config.js _
Lancez Webpack avec la commande webpack. Si tout a bien fonctionné, rendez-vous dans votre fichier bundle. js : vous verrez que le fichier a été minifié et ne tient plus que sur une seule ligne.
environnements de production et de développement
C’est bien beau tout ça, mais comment est-ce que vous allez vous y retrouver dans tout ce code minifié ? Pas facile de débugger. Nous allons donc mettre en place un environnement de développement dans lequel rien ne sera minifié.
Rendez-vous donc dans votre fichier package.json et ajoutez les lignes suivantes à votre objet script; sous linux :
Sous Windows commencez par installer la dépendance cross-en. Ensuite, modifiez votre fichier package.json de la manière suivante :
npm install --save-dev cross-env
Lancez Webpack en faisant npm run prod. Si vous avez bien fait, vous devriez voir NODE_ENV=production webpack --progress dans votre terminal. C’est bien, mais ça ne fait actuellement rien de plus que notre commande npm run start en terme de différenciation d’environnement. Nous allons donc y remédier en tapant cette condition sous notre module.exports de notre fichier webpack.config.js
_webpack.config.js _
En gros, on dit dans cette condition que si on dans notre environnement de production, alors minifie nos fichiers en utilisant le plugin UglifyJS. Du coup, n’oubliez pas de supprimer new UglifyJSPlugin() de votre array plugins sinon vos fichiers seront minifiés dans tous les cas.
Minifier les fichiers CSS
installer une nouvelle dépendance :
npm install optimize-css-assets-webpack-plugin --save-dev
Ensuite, il va falloir créer une constante dans votre fichier webpack.config.js: et on appelle le plugin dans notre condition :
Vous avez désormais 3 moyens de lancer Webpack :
Bonus
Votre terminal affiche désormais tous les détails dont vous avez besoin :
Mais il faut avouer que ce n’est pas très clair, non ? Que diriez-vous de pimper Webpack pour qu’il ressemble à un dashboard.
npm install webpack-dashboard --save-dev
webpack.config.js
Modifiez l’objet scripts de package. json en ajoutant webpack-dashboard pour que le dashboard se lance lorsque vous lancez votre serveur avec npm run start :
Vous en savez désormais plus sur Webpack et vous avez surtout réussi à configurer votre starter que vous pourrez utiliser pour votre prochain projet !
réf: Débuter avec Webpack
https://www.alsacreations.com/tuto/lire/1754-debuter-avec-webpack.html