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, ...

https://www.w3schools.com/jsref/dom_obj_event.asp

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
  1. Créer et lancer le projet.
  2. Créer votre premier composant: Recipe.
  3. Créer des modèles: Recipe, Recipe Ingredients, Ingredients.
  4. Créer une constante de type Recipe dans votre composant.
  5. Afficher les données dans le composant.
  6. Créer un composant parent: Recipe list.
  7. Mettre une liste de Recipe dans le componant Recipe List.
  8. Afficher une liste de Recipe.
  9. Ajouter un bouton toggle pour afficher ou retirer les instructions / ingrédients.
  10. 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

  1. Créer un service : Recipe.
  2. Mettre la liste de recettes dans le service en asynchrone.
  3. Faire sa première requête http sur le back.
  4. Ajouter le module de routage.
  5. 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

  1. Créer une nouvelle page d'ajout de recette.
  2. Créer un formulaire pour créer une recette avec un nom, une url d'image, et une description.
  3. Ajouter une validation pour rendre les champs obligatoires.
  4. Ajouter une dropdown pour sélectionner un ou plusieurs ingrédients.
  5. 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