Julien Lepasquier & Jean-Marc Debicki
Excilys - 2021
NGULAR
v12.x.x
npm install @angular/cli
Initial Request
Form Post
Client
Server
Page reload !
Initial Request
XHR
{ ... } JSON
Client
Server
Sur-ensemble de Javascript
Open source
Développé par Microsoft
Transpilé en Javascript
Typage statique
ECMAScript 6
Class, Interfaces
export class MonComponent {
private maVariable: number | boolean | string = 'abc';
protected array: any[];
public abc: null | undefined;
static readonly CONST = "CONSTANTE";
constructor(private readonly service: Service) {}
maFonction(attr: number): void {
const constante = 123;
let variable = "abc";
let monArrowFunction = (a, b) => a+b;
}
}
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
{ }
Life Cycle Hooks
@Component({
selector: 'parent-component',
template: '<child-component></child-component>',
styleUrls: string[],
...
})
class ParentComponent {}
@Component({
selector: 'child-component',
template: 'Salut les grands malades !'
})
class ChildComponent {}
@Injectable({
providedIn: 'root'
})
class MonService {}
class MonComponent {
constructor(private readonly service: Service) {}
}
@NgModule({
declarations: any[],
imports: any[],
exports: any[],
providers: any[],
...
})
export MonMondule {}
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
}
}
<p appHighlight>Je suis surligné</p>
<p>Pas moi</p>
<div *ngIf="expressionBooleenne">
Hello
</div>
<div [hidden]="expressionBooleenne">
Hello
</div>
<ul>
<li *ngFor="let item of itemList">
{{ item }}
</li>
</ul>
<div>
Hello {{ name }}
</div>
<button (click)="doSomething()">
Click me !
</button>
Liste des HTML DOM Events sans le "on" :
click, dblclick, mousedown, keypress, change, resize, blur, focus, ...
{{ value }}
[property]="value"
(event)="handlerFunction($event)"
[(ngModel)]="property"
DOM
Component
Template
< >
Component
{ }
Injector
Service
{ }
LoginModule
{}
{{ person.name }}
(click)="handle()"
Metadata
Directive
{ }
Metadata
UserModule
{}
CardModule
{}
ListModule
{}
Génération "boilerplate" :
ng new first-app
> CSS ?
> SCSS
> Routing ?
> no
Lancement du serveur :
ng serve
Ou :
npm start
Arborescence
package.json
Un peu d'aide côté design : Angular Material
Installation :
ng add @angular/material
Ajout d'un header
Dans src/app, créer le composant Header :
ng generate component header
Dans src/app :
ng generate module custom-material
Affichage d'une recette
Composant Recipe :
ng generate component recipe
Création du modèle
recipe.model.ts
recipe-ingredient.model.ts
ingredient.model.ts
Création du modèle
import { RecipeIngredient } from './recipe-ingredient.model';
export class Recipe {
id?: number;
name: string;
picture: string;
description: string;
ingredients: RecipeIngredient[];
instructions: string[];
}
Création du modèle
import { Ingredient } from './ingredient.model';
export class RecipeIngredient {
id?: number;
ingredient: Ingredient;
quantity: number;
unit: string;
}
Création du modèle
export class Ingredient {
name: string;
id?: number;
}
Affichage des recettes
Paramètre d'entrée du composant :
@Input()
recipe: Recipe;
Affichage d'une liste de recettes
Composant Recipe List :
ng generate component recipe-list
Affichage des recettes
Afficher la liste :
<app-recipe *ngFor="let recipe of recipes"
[recipe]="recipe">
</app-recipe>
Affichage des recettes
Afficher / masquer les détails :
<button (click)="toggleExpand()">
See more
</button>
Service RecipeService
Création du service :
ng generate service recipe
Service RecipeService
Refacto permettant de gérer l'asynchrone :
getRecipes(): Observable<Recipe[]> {
return of(RECIPES);
}
Récupération des données de l'Observable
Dans un composant appelant le getRecipes() du service.
this.recipeService.getRecipes().subscribe(
(result: Recipe[]) => {
// Traiter le résultat
},
(error) => {
// Traiter l'erreur
}
);
Passage au HttpClient :
imports: [
...,
HttpClientModule,
...
]
AppModule :
Passage au HttpClient :
private recipeUrl = 'api/recipes';
getRecipes(): Observable<Recipe[]> {
return this.http.get<Recipe[]>(this.recipeUrl);
}
RecipeService :
Passage au HttpClient :
getRecipe(id: number): Observable<Recipe> {
return this.http.get<Recipe>(`${ this.recipeUrl }/${ id }`);
}
RecipeService, ajout d'une requêt "by ID" :
Routing :
ng generate module app-routing --flat
AppRoutingModule
Routing :
const routes: Routes = [
{
path: 'recipes',
component: RecipesComponent,
pathMatch: 'full'
},
{
path: '**',
redirectTo: 'recipes',
pathMatch: 'full'
}
];
AppRoutingModule
Routing :
@NgModule({
exports: [
RouterModule
],
imports: [
RouterModule.forRoot(routes)
]
})
AppRoutingModule
Routing :
<router-outlet></router-outlet>
Point d'entrée des routes
Routing :
Refacto : Modularisation de Recipe
Nouvelle page : Détail d'une recette
TIPS :
Nouvelle page : Formulaire d'ajout de recette
TIPS :
Suppression de recette
Reactive Forms : Validation dynamique de formulaires
https://angular.io/guide/reactive-forms#add-a-formgroup
Pipes : Transformer les données
@Pipe({
name: 'orderBy'
})
export class OrderByPipe implements PipeTransform {
transform(param : InputType): OutputType {
doSomething();
return newValue;
}
}
Tests unitaires !
Karma + Jasmine
Animations : Animer les composants
https://angular.io/guide/animations#setup