IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

TEST1 - Tutoriel sur une introduction au framework web Angular

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum. 9 commentaires Donner une note à l´article (5)

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Étude de cas 1 : La réutisabilité des composants Web

Nous avons vu que réutiliser les composants Web était une bonne pratique car cela améliore la maintenabilité et augmente la productivité.

  • nous allons donc dans ce chapitre écrire un exemple de composant Web réutilisable ;
  • ce composant affichera une liste d'élements et renverra la choix que l'utilisateur à selectionné, un peu comme un groupe de radio bouton.

I-A. à savoir

  • pour une réutisabilité optimale, le composant doit se contenter de ne faire que sa propre fonctionnalité, celui d'afficher une liste et retourner le choix de l'utilisateur ;
  • en somme, ce composant :
  • ne devra pas contenir la liste des éléments à afficher ;
  • ne devra pas aller chercher la liste des éléments à afficher (mais par contre, il le recevra)

I-B. Description

  • le composant réutilisable sera contenu dans le dossier : /shared
  • pour le design, le composant utilisera un composant UI d'Angular Material ;
  • voici le détail du composant Web réutilisable :
  • réceptionne la liste des éléments (d'un certain type) à afficher ;
  • réceptionne l'élément par défaut qui doit être sélectionné (possible qu'il n'y ai aucun élément) ;
  • réceptionne le nom du groupe auquel appartient la liste ;
  • sélectionne l'item par défaut ou celui enregistré (lors d'une précédente sélection utilisateur) ;
  • renvoi le choix de l'utilisateur au composant qui a fait appel à ce composant ;
  • contient le type de donnée que le composant manipule. Ainsi de l'extérieur, on sait de quel type doivent être les données que l'on doit fournir au composant ;
  • enregistre le choix de l'utilisateur ;
  • le design du composant sera géré par Angular Material.

I-B-1. Remarques

  • pour le design, on va utiliser le toggle button d'Angular material ;
  • https://material.angular.io/components/button-toggle/overview
  • lors du routing à chaque accès à une page : page1 ou page2, les composants pages s'initialisent et donc ne conserve pas l'état de la sélection. C'est pour cela qu'on utilise un service pour stocker le choix de l'utilisateur et ainsi l'utiliser pour initialiser le composant avec la bonne sélection.

I-B-2. Inventaires

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
- /shared/button-toggle-mat
  - /components/button-toggle-mat.component.ts              // le composant
  - /services/stored.service.ts                             // pour enregistrer le choix de l'utilisateur
  - /models/i-item-btm.ts                                   // le type de donnée que le composant manipule
  - button-toggle-mat.module.ts                             // déclare le composant et le service. De plus, exporte le composant

- /pages
  - /page1                                      // composant avec une liste d'item 'quelconque' ayant comme nom de groupe : 'choice'
  - /page2                                      // composant avec une liste d'item 'couleur' ayant comme nom de groupe : 'color'
                                                // composant avec une liste d'item 'quelconque' ayant comme nom de groupe : 'choice'

I-C. Pratique

Créer un nouveau projet : angular-re-use1

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
ng new angular-re-use1
routing ? YES
SCSS

ng add @angular/material

ng g m pages --module=app
ng g m pages/page1 --module=pages
ng g m pages/page2 --module=pages
ng g c pages/page1 --module=page1
ng g c pages/page2 --module=page2

ng g m shared/button-toggle-mat --module=app
ng g c shared/button-toggle-mat/components/button-toggle-mat --module=button-toggle-mat
ng g i shared/button-toggle-mat/models/i-item-btm
ng g s shared/button-toggle-mat/services/stored

app.module.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
//
import { PagesModule } from './pages/pages.module';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    NoopAnimationsModule,
    //
    PagesModule,                // pour le routing
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app-routing.module.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
//
import { Page1Component } from 'src/app/pages/page1/page1.component';
import { Page2Component } from 'src/app/pages/page2/page2.component';

const routes: Routes = [
  { path: 'page1', component: Page1Component },
  { path: 'page2', component: Page2Component },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

app.component.html

 
Sélectionnez
1.
2.
3.
4.
5.
6.
<ul>
  <li><a [routerLink]="['/page1']">aller à page1</a></li>
  <li><a [routerLink]="['/page2']">aller à page2</a></li>
</ul>

<router-outlet></router-outlet>

\data\param.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
export const ItemsChoice = [
  {key: 'K0', value: 'Aucun'},
  {key: 'K1', value: 'Choix 1'},
  {key: 'K2', value: 'Choix 2'},
  {key: 'K3', value: 'Choix 3'},
];

export const ItemsColor = [
  {key: 'NONE', value: 'Aucune'},
  {key: 'V', value: 'Vert'},
  {key: 'R', value: 'Rouge'},
];

\pages\page1\page1.module.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
//
import { ButtonToggleMatModule } from '../../shared/button-toggle-mat/button-toggle-mat.module';
import { Page1Component } from './page1.component';


@NgModule({
  declarations: [Page1Component],
  imports: [
    CommonModule,
    //
    ButtonToggleMatModule,            // utilise le composant réutilisable du module          
  ]
})
export class Page1Module { }

\pages\page2\page2.module.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
//
import { ButtonToggleMatModule } from '../../shared/button-toggle-mat/button-toggle-mat.module';
import { Page2Component } from './page2.component';


@NgModule({
  declarations: [Page2Component],
  imports: [
    CommonModule,
    //
    ButtonToggleMatModule,            // utilise le composant réutilisable du module

  ]
})
export class Page2Module { }

\pages\pages.module.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
//
import { Page1Module } from './page1/page1.module';
import { Page2Module } from './page2/page2.module';

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    //
    Page1Module,
    Page2Module,

  ]
})
export class PagesModule { }

\pages\page1\page1.component.html

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<p>page1 works!</p>

html:ef648c4b35586de068ddd565a237c04bc83f9409

<app-button-toggle-mat
  [items]="choices"
  [selectedItem]="selectedChoice"
  [group]="'choice'"
  (selectedItemEvent)="onSelectedChoice($event)"
></app-button-toggle-mat>

(1) choice={{selectedChoice|json}}

\pages\page1\page1.component.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
import { Component, OnInit } from '@angular/core';
import { IItemBtm } from '../../shared/button-toggle-mat/models/i-item-btm';
import { ItemsChoice } from '../../data/param';

@Component({
  selector: 'app-page1',
  templateUrl: './page1.component.html',
  styleUrls: ['./page1.component.scss']
})
export class Page1Component implements OnInit {
  choices: IItemBtm[];                // on éxige que ces éléments du tableau doivent être du type : IItemBtm
                                      // ainsi, il ne peut y avoir d'erreur car notre composant ne gère que ce type de donnée : IItemBtm
  selectedChoice: IItemBtm;           // on fourni l'élément par défaut au composant réutilisable (non obligatoire)  (2)
                                      // et en même temps va contenir le choix de l'utilisateur

  constructor() {
  }

  ngOnInit(): void {
    this.choices = ItemsChoice;                // on fourni une liste d'éléments au composant réutilisable
    this.selectedChoice = this.choices[0];     // (2) par défaut, l'élément qui sera sélectionné sera le premier élément (non obligatoire)
  }

  onSelectedChoice(item: IItemBtm) {           // lien avec le composant réutilisable enfant
    this.selectedChoice = item;                // (1) on réceptionne le choix de l'utilisateur pour l'afficher dans la vue
    //
    //  traitement
    //
  }
}

\pages\page2\page2.component.html

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<p>page2 works!</p>

html:a53a986be9b1f52de0cf56f27634e343dae5b05d

<app-button-toggle-mat
  [items]="colors"
  [group]="'color'"
  (selectedItemEvent)="onSelectedColor($event)"
></app-button-toggle-mat>

(1) Couleur={{selectedColor|json}}

html:3087918116cd08eac5d04ee6ca68db7e1be92024


html:ef648c4b35586de068ddd565a237c04bc83f9409

<app-button-toggle-mat
  [items]="choices"
  [selectedItem]="selectedChoice"
  [group]="'choice'"
  (selectedItemEvent)="onSelectedChoice($event)"
></app-button-toggle-mat>

(1) choice={{selectedChoice|json}}

\pages\page2\page2.component.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
import { Component, OnInit } from '@angular/core';
import { IItemBtm } from '../../shared/button-toggle-mat/models/i-item-btm';
import { ItemsColor } from '../../data/param';
import { ItemsChoice } from '../../data/param';

@Component({
  selector: 'app-page2',
  templateUrl: './page2.component.html',
  styleUrls: ['./page2.component.scss']
})
export class Page2Component implements OnInit {

  //  Choix d'une couleur
  colors: IItemBtm[];                         // on éxige que les éléments du tableau doivent être du type : IItemBtm
  selectedColor: IItemBtm;                    // pour contenir le choix de l'utilisateur

  //  Choix d'un item quelconque
  choices: IItemBtm[];                        // on éxige que les éléments du tableau doivent être du type : IItemBtm
  selectedChoice: IItemBtm;                   // pour contenir le choix de l'utilisateur

  constructor() { }

  ngOnInit(): void {                          // initialisation des données
    //  Choix d'une couleur
    this.colors = ItemsColor;                 // on fourni une liste d'éléments au composant réutilisable
                                              // sans valeur par défaut
    //  Choix d'un item quelconque
    this.choices = ItemsChoice;               // on fourni une liste d'éléments au composant réutilisable
    this.selectedChoice = this.choices[0];    // avec une valeur par défaut
  }

  //  Choix d'une couleur
  onSelectedColor(item: IItemBtm) {           // lien avec le composant réutilisable enfant
    this.selectedColor = item;                // (1) on réceptionne le choix de l'utilisateur pour l'afficher dans la vue
    //
    //  traitement
    //
  }

  //  Choix d'un item quelconque
  onSelectedChoice(item: IItemBtm) {           // lien avec le composant réutilisable enfant
    this.selectedChoice = item;                // (1) on réceptionne le choix de l'utilisateur pour l'afficher dans la vue
    //
    //  traitement
    //
  }
}

\shared\button-toggle-mat\button-toggle-mat.module.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
//
import { ButtonToggleMatComponent } from './components/button-toggle-mat/button-toggle-mat.component';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { StoredService } from './services/stored.service';


@NgModule({
  declarations: [ButtonToggleMatComponent],
  imports: [
    CommonModule,
    MatButtonToggleModule,              // on importe uniquement le module : MatButton d'Angular Material pour pouvoir utiliser son composant

  ],
  exports: [
    ButtonToggleMatComponent,           // on exporte le composant pour qu'il soit utilisable lors d'un import
  ],
  providers: [StoredService, ]          // les composants de ce module auront accès à cette instance du service
})                                      // donc : ButtonToggleMatComponent des pages : page1 et page2 aura accès à cette instance
export class ButtonToggleMatModule { }

\shared\button-toggle-mat\models\i-item-btm.ts

 
Sélectionnez
1.
2.
3.
4.
export interface IItemBtm {
  key: string;
  value: string;
}

\shared\button-toggle-mat\services\stored.service.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
import { IItemBtm } from '../models/i-item-btm';

export class StoredService {

  selectedItemGroup: IItemBtm[] = [];       //  un tableau contenant les couples :  'nom de groupe': sélection utilisateur

  constructor() { }

  getSelectedItem(group: string): IItemBtm {          // on récupère le choix utilisateur par son groupe
    return (undefined !== this.selectedItemGroup[group]) ? this.selectedItemGroup[group] : undefined;
  }

  setSelectedItem(item: IItemBtm, group: string) {    // on enregistre le choix utilisateur par son groupe
    this.selectedItemGroup[group] = item;
  }

  getInitializedItem(defaultItem: IItemBtm, group: string): IItemBtm {      // on calcul en fonction de la valeur par défaut et de la valeur enregistré du tableau
    if (defaultItem && this.getSelectedItem(group) === undefined) {         // si il y a une valeur par défaut et pas de sélection utilisateur enregistré alors...
      this.setSelectedItem(defaultItem, group);                             // c'est le choix par défaut qui est pris en compte
      return defaultItem;
    }

    return this.getSelectedItem(group);                                     // sinon c'est le choix enregistré dans le tableau qui est pris en compte
  }
}

\shared\button-toggle-mat\components\button-toggle-mat\button-toggle-mat.component.html

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<p>
  Votre choix ? &nbsp;
  <mat-button-toggle-group name="fontStyle" aria-label="Font Style" #group="matButtonToggleGroup" [value]="selectedItem?.key" (change)="onChange($event)">
    <ng-container *ngFor="let item of items">
      <mat-button-toggle value="{{item.key}}" >{{item.value}}</mat-button-toggle>
    </ng-container>
  </mat-button-toggle-group>
</p>

\shared\button-toggle-mat\components\button-toggle-mat\button-toggle-mat.component.ts

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
import { IItemBtm } from '../../models/i-item-btm';
import { StoredService } from '../../services/stored.service';

@Component({
  selector: 'app-button-toggle-mat',
  templateUrl: './button-toggle-mat.component.html',
  styleUrls: ['./button-toggle-mat.component.scss'],
})
export class ButtonToggleMatComponent implements OnInit {
  @Input() items: IItemBtm[];                                   // réceptionne la liste des éléments
  @Input() selectedItem: IItemBtm;                              // l'élément qui doit être sélectionné par défaut
  @Input() group: string;                                       // le groupe auquel appartient les éléments
  @Output() selectedItemEvent = new EventEmitter<IItemBtm>();   // envoi le choix de l'utilisateur au parent : page1 ou page2

  constructor(private storedService: StoredService) { }

  ngOnInit(): void {                                            // à l'initialisation du composant
    this.selectedItem = this.storedService.getInitializedItem(this.selectedItem, this.group);   // on calcul quel item est sélectionné au 1er affichage
    this.selectedItemEvent.emit(this.selectedItem);                                             // on retourne cet item au parent : page1 ou page2
  }

  onChange(event: any) {                                        // quand un choix utilisateur est fait
    // event.value ne contient que : key
    // on veut pouvoir retourner l'objet entier (key + value)
    // donc on va le chercher dans la liste des items à partir de sa clé : key
    const item: IItemBtm = this.items.filter((item: IItemBtm) => item.key == event.value)[0];   // on parcourt la liste des items et si on trouve la correspondance avec: key
                                                                                                // alors on retourne l'objet trouvé
    this.storedService.setSelectedItem(item, this.group);           // on enregistre le choix
    this.selectedItemEvent.emit(item);                              // on retourne le choix de l'utilisateur au parent : page1 ou page2
  }
}

I-D. Résultat

  • quand on sélectionne un choix celui çi est envoyé à son parent : page1.component ou page2.component ;
  • remarquez que de page1 à page2 et vice versa, le choix 'quelconque' garde la sélection que l'on a choisie grace au même nom de groupe.

I-E. Conclusion

  • le composant Web peut être utilisé plusieurs fois, il suffit de copier coller le dossier : /button-toggle-mat dans un autre projet et l'utiliser tel quel ;
  • comme le composant ne dépend pas d'une liste défini, on peut lui transmettre n'importe quelle liste à condition qu'elle respecte le modèle ;
  • il suffit de lui transmettre une liste à afficher, un nom de groupe et si besoin un élément par défaut ;
  • à savoir que lors du routing, l'accès à une page engendre l'initialisation de son composant page et de ses données.
  • pour pouvoir enregistrer des données afin de les récupérer lors de l'initialisation d'un composant page on se sert d'un service pour stocker les données (car son instance est un singleton).

II. Mise en production : Firebase hosting

Utilisons le service Hosting de firebase pour mettre en production une application Angular.
Ce service est gratuit et limité mais cela suffit largement pour tester.

(1)
On va utiliser le projet : angular-re-use1 pour le mettre en production

copier / coller le projet : angular-re-use1 et renommez le dossier en : angular-hosting1

sur le nouveau dossier : angular-hosting1 faite une recherche global et remplacer tous les mots : 'angular-re-use1' en 'angular-hosting1'
(sur visual studio code -> clique droit sur le dossier -> find in folder -> angular-re-use1 en : angular-hosting1)

(2) ou utiliser n'importe quel projet qui tourne en local

II-A. Pratique

Créer un compte et se connecter :

https://firebase.google.com/

  • une fois connecté, il faut créer un projet firebase ;
  • ce projet proposera divers services : base de données firebase, google analytics, hosting, functions....
  • nous allons juste utiliser le servide Hosting pour deployer notre application.
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
-> Accéder à la console -> Ajouter un projet -> nom du projet : 'hosting1' -> continuer

Configurer Google Analytics -> Créer un compte : 'hosting1-google-analytics' -> enregistrer 

-> créer un projet

menu de gauche -> hosting -> commencer

On installe en global les outils pour angular-cli afin de pouvoir lancer les commandes firebase :

 
Sélectionnez
1.
npm install -g firebase-tools@latest

il faut se connecter afin que angular-cli soit lié avec le compte firebase que vous avez créé :

 
Sélectionnez
1.
firebase login                          // le navigateur chrome va s'ouvrir pour vous demandez de vous connecter

La commande suivante va éffectuer quelques modifications des fichiers de votre projet pour initialiser le déploiement :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
firebase init
? Are you ready to proceed? (Y/n)   Y
-> Use an existing project          Y

(*) Hosting             <barre espace>      pour sélectionner
                        <touche entrée>     pour valider

-> Select a default Firebase project for this directory:                      hosting1-......

-> ? What do you want to use as your public directory?                        dist/angular-hosting1

-> ? Configure as a single-page app (rewrite all urls to /index.html)? (y/N)  Y

-> ? Set up automatic builds and deploys with GitHub? (y/N)                   N

La première fois et à chaque fois que vous mettez en production, il faut lancer les 2 commandes suivantes :

 
Sélectionnez
1.
2.
ng build --prod                             // toujours builder en : --prod avant le deploiement
firebase deploy

II-B. à savoir

C'est le contenu du dossier : /dist/angular-hosting1 qui est déployé dans le cloud Hosting

II-C. Résultat

Et voilà, plus qu'à accéder à l'application en ligne
https://hosting1-.......web.app/#/ (voir le lien affiché à la fin du : firebase deploy)

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2020 iner dukoid. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.