Tribu Angular
Oxyl
NGULAR

Présentation


Un peu d'histoire ...
- Juin 2012 : AngularJS (Miško Hevery et Adam Abronsw puis Google)
- Septembre 2016 : Angular 2
- Mars 2017 : Angular 4
- ...
- Novembre 2023 : Angular 17

Application traditionnelle
- Échanges client-serveur lourds
- Rechargement de la page
- Pas d'offline
Initial Request
Form Post
Client

Server

Page reload !

Single Page Application
- Fluidité
- User experience (UX)
- Offline
Initial Request
XHR
{ ... } JSON
Client

Server

Typescript
- Transpilé en JavaScript.
- Typage fort : Autocomplétion, Erreur à la compilation, Maintenabilité
- Suit les normes ECMAScript.
- Javascript : typage dynamique => Typescript : typage statique.
- Développé par Microsoft et Open source

Survol d'Angular


Vue d'ensemble
Template
< >
Component
{ }
Injector
Service
{ }
LoginModule
{}
{{ person.name }}
(click)="handle()"
Metadata
Directive
{ }
Metadata
UserModule
{}
CardModule
{}
ListModule
{}

Module
@NgModule({
declarations: any[],
imports: any[],
exports: any[],
providers: any[],
...
})
export class MonMondule {}
Component

Life Cycle Hooks
@Component({
selector: 'parent-component',
template: '<child-component></child-component>',
styleUrls: string[],
...
})
export class ParentComponent {}
@Component({
selector: 'child-component',
template: 'Salut les grands malades !'
})
export class ChildComponent {}
Injectable
@Injectable({
providedIn: 'root'
})
export class MonService {}
export class MonComponent {
constructor(private readonly service: Service) {}
}
Directive
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
}
}<p appHighlight>Je suis surligné</p>
<p>Pas moi</p>
Data binding
{{ value }}
[property]="value"
(event)="handlerFunction($event)"
[(ngModel)]="property"
DOM
Component

Template binding {{ }}
<div>
Hello {{ name }}
</div>
Dom events
<button (click)="doSomething()">
Click me !
</button>Liste des HTML DOM Events sans le "on" :
click, dblclick, mousedown, keypress, change, resize, blur, focus, ...

Component tree
Root Component
Root Template
< >
Root Class
{ }
Child A Component
Child A Template
< >
Child A Class
{ }
Child B Component
Child B Template
< >
Child B Class
{ }
Grandchild Component
Grandchild Template
< >
Grandchild Class
{ }

Arborescence


Premier pas

Créer et lancer un projet
ng new first-app
cd first-app
npm install
npm start
Créer un composant
ng generate component components/recipe
Créer un modèle
export interface Ingredient {
name: string;
id?: number;
}
Créer un module
ng generate module custom-material
Créer une constante

public recipe: Recipe = {
description: "Les jolis voyages",
instructions: [
"Verser l’absinthe dans un verre. L’idéal serait un verre à absinthe, mais une petite coupe à dégustation peut faire l’affaire. Déposer la cuillère à absinthe sur le verre et y déposer le cube de sucre.",
"Faire couler l’eau de source glacée très doucement, quelques gouttes à la fois, sur le cube de sucre. Enfin, remuer l’absinthe avec la cuillère et servir.",
"À savourer en bonne compagnie, à 17 h, en écoutant de l’accordéon et en récitant Les Fleurs du Mal.",
""
],
name: "l'absinthe",
ingredients: [
{
ingredient: {
name: "absinthe"
},
quantity: 1,
unit: "Oz"
},
{
ingredient: {
name: "sucre"
},
quantity: 1,
unit: "cube"
},
{
"ingredient": {
"name": "eau de source glacée"
},
"quantity": 3,
"unit": "oz"
},
{
"ingredient": {
"name": "cuillère à absinthe perforée"
},
"quantity": 1,
"unit": "cuillère"
}
],
"picture": "https://boiremixologie.com/files/medias/_imageCarrousel/absinthe_cocktail_recette_classique_verte_fee_tradition.jpg"
}*ngFor
<ul>
<li *ngFor="let item of itemList">
{{ item }}
</li>
</ul>
*ngIf
<div *ngIf="expressionBooleenne">
Hello
</div>
Import une donnée du parent
Paramètre d'entrée du composant :
@Input()
recipe: Recipe;
<mon-composant-enfant [recipe]="maRecette"></mon-composant-enfant>Design
Un peu d'aide côté design : Angular Material
Installation :
ng add @angular/material

- Créer et lancer le projet.
- Créer votre premier composant: Recipe.
- Créer des modèles: Recipe, Recipe Ingredients, Ingredients.
- Créer une constante de type Recipe dans votre composant.
- Afficher les données dans le composant.
- Créer un composant parent: Recipe list.
- Mettre une liste de Recipe dans le componant Recipe List.
- Afficher une liste de Recipe.
- Ajouter un bouton toggle pour afficher ou retirer les instructions / ingrédients.
- Ajouter une librairie de composants stylisés.
Pour les rapides: Mettre Angular Material dans un module; Créer un composant Header; Styliser tes composants
A vous de jouer
http://52.47.189.244:8080/swagger-ui.html#/
On continue


Créer un service
ng generate service recipe
L'asynchrone en JS:
Les promesses
new Promise((resolve, reject) => { /* execution en asynchrone */ })
.then(result => { /* Traiter le résultat */ })
.catch(error => { /* Traiter l'erreur */ });
RXJS
La library JAVASCRIPT pour faciliter l'utilisation de l'asynchrone.
getRecipes(): Observable<Recipe[]> {
return of(RECIPES);
}
Observable et Subject :
this.recipeService.getRecipes().subscribe({
next: (result: Recipe[]) => { /* Traiter le résultat */ },
error: (error) => { /* Traiter l'erreur */ }
});Pour en arriver où ?
Http Client
Le Module : HttpClientModule

private recipeUrl = 'api/recipes';
constructor(private http: HttpClient) { }
getRecipes(): Observable<Recipe[]> {
return this.http.get<Recipe[]>(this.recipeUrl);
}Le routage 1/2
Créer un module dédié : AppRoutingModule
L'importer dans le module principal

@NgModule({
exports: [
RouterModule
],
imports: [
RouterModule.forRoot(routes)
]
})
export class AppRoutingModule { }Le routage 2
Définir une stratégie de route :

const routes: Routes = [
{
path: 'recipes',
component: RecipeListComponent,
pathMatch: 'full'
},
{
path: '**',
redirectTo: 'recipes',
pathMatch: 'full'
}
];<router-outlet></router-outlet>Point d'entrée des routes

- Créer un service : Recipe.
- Mettre la liste de recettes dans le service en asynchrone.
- Faire sa première requête http sur le back.
- Ajouter le module de routage.
- Créer une nouvelle page pour voir les détails d'une recette (Get by ID + page de routage spécifique + composant Recipe Detail).
Pour les rapides : Lors de la récupération des recettes, ajouter un tri par nom; Sur la page recipe ID, ajouter un "guard" dans le router pour vérifier que la recette existe
A vous de jouer
http://52.47.189.244:8080/swagger-ui.html#/
TIPS :
- Créer une nouvelle route recipes/:id permet de variabiliser le param id
- L'attribut routerLink="/recipes/{{ recipe.id }}" permet de naviguer vers une la nouvelle route
- this.route.snapshot.paramMap.get('id') permet de récupérer le paramètre de la route à la création du composant (this.activatedRoute doit être injecté depuis le service Angular ActivatedRoute)
Encore un peu plus loin


Reactive Forms
Validation dynamique de formulaires
https://angular.io/guide/reactive-forms


- Créer une nouvelle page d'ajout de recette.
- Créer un formulaire pour créer une recette avec un nom, une url d'image, et une description.
- Ajouter une validation pour rendre les champs obligatoires.
- Ajouter une dropdown pour sélectionner un ou plusieurs ingrédients.
- Ajouter une option de suppression de recette sur la page principale.
Pour les rapides : Sur le formulaire d'ajout, forcer l'utilisation d'au moins une instruction et d'au moins un ingrédient, qu'une instruction ne peut pas être vide etc...
A vous de jouer
http://52.47.189.244:8080/swagger-ui.html#/
https://angular.io/guide/reactive-forms
Pour les rapides


Pipes
@Pipe({
name: 'orderBy'
})
export class OrderByPipe implements PipeTransform {
transform(param : InputType): OutputType {
doSomething();
return newValue;
}
}
Tests unitaires !

Animations : Animer les composants
https://angular.io/guide/animations#setup

Les liens utiles

Copy of Formation Angular v2
By yoannbeunardeau
Copy of Formation Angular v2
- 180