Julien Lepasquier & Jean-Marc Debicki
Excilys - 2021
NGULAR
v12.x.x
npm install @angular/cliInitial 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 ?
> noLancement du serveur :
ng serveOu :
npm startArborescence
package.json
Un peu d'aide côté design : Angular Material
Installation :
ng add @angular/materialAjout d'un header
Dans src/app, créer le composant Header :
ng generate component headerDans src/app :
ng generate module custom-materialAffichage d'une recette
Composant Recipe :
ng generate component recipeCré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-listAffichage 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 recipeService 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 --flatAppRoutingModule
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