Angular

Samuel eyre

CC BY-NC-SA samueleyre 2021

Concepts

Index

Ressources

COMPONENT

Une classe liée à du html et du css

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


  constructor() {

  }

  ngOnInit() {
  
  }


}

Le selector permet d'appeler le component dans du html avec : "<app-un-nom></app-un-nom> "

ngOnInit est appelé quand quand le DOM est chargé

MODULE

Une classe rattachée à des dépendances locales  ou externes qui délimite un domaine de l'application de manière à créer des blocks le plus possible indépendants

@NgModule({
  declarations: [
UnComponent,
  ],
  imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
CoreModule,
SharedModule,
  ],
  exports: [
UnComponent,
  ],

})
export class AppModule { }

Les components sont déclarés dans "declarations" afin d'être compilés dans le module.

Les dépendances externes sont appelées dans "imports"

Pour rendre accessible des components dans les modules qui importent AppModule il faut les appeler dans "exports"

ROUTER

Lie les components avec les chemins de l'url 

Cette balise est remplacée par le html chargé par le component. 

<router-outlet></router-outlet>

Le Lazy Loading

Pour charger des modules dynamiquement, en fonction des chemins de l'url

const routes: Routes = [
  {
    path: 'direct', component: DirectComponent
  },
  {
    path: 'sous',
    loadChildren: () => import('./sous/sous.module').then(mod => mod.SousModule),
  },
];

Le chemin '/direct' chargement rapidement car le component est déjà téléchargé par le navigateur.  

Les chemins '/sous/**' chargement après téléchargement du module 'SousModule'

SERVICE

Un classe injectable qui sert à processer des données ou stocker des données réutilisables

On peut injecter des services dans des components, dans d'autres services ou tout autre classe angular, mais attention aux dépendances circulaires !

 

"providedIn" définit l'accessibilité de la classe. Si on met "root", elle sera injectable dans toute l'application.

 

 

@Injectable({
  providedIn: 'root'
})
export class UnService {

  constructor(
    private autreService: AutreService,
  ) { }
  
}

OBSERVABLE

Un flux d’événements angular asynchrone auquel on peut s'inscrire

  const observable$ = of(1, 2, 3);

    observable$.subscribe((val) => {
      console.log(val);
    });

"of()" permet de créer un observable avec un flux de données : "1,2,3" ( utile pour cet exemple )

Subscribe() sert à observer ce qu'il se passe et récupérer les données

OBSERVABLE

Dans le cas d'une requête

  const observable$ = this.httpClient.get('/api-path');

  observable$.subscribe((val) => {
      console.log(val);
  });

Ici subscribe sert à exécuter la requête et récupérer la réponse.

PIPE ( opérateur RXJS )

Modifie "à la volé" les données dans un flux de données.

  const observable$ = of(1, 2, 3);

    observable$.pipe(map((val) => {
      return val * 2;
    })).subscribe((val) => {
      console.log(val);
    });

On modifie chaque donnée avec la fonction "map()"  

Pour découvrir toutes les opérations possibles ( telle que "map()"), voir la doc de rxjs.

SUBJECT

Un Observable qui permet d'avoir plusieurs "observers" ( et donc plusieurs subscribe )

const subject = new Subject<number>();

subject.subscribe((val) => {
	console.log('subject subscribe 1, val :', 
    val);
});

subject.subscribe((val) => {
	console.log('subject subscribe 2, val :', 
    val);
});


const observable$ = of(1, 2, 3).pipe(
      tap(val => {
        console.log('source :', val);
        subject.next(val);
      })
    ).subscribe();
 const observable$ = of(1, 2, 3).pipe(
      tap(val => {
        console.log('source :', val);
      })
    );

observable$.subscribe((val) => {
  console.log('obs subscribe 1', val);
});

observable$.subscribe((val) => {
  console.log('obs subscribe 2', val);
});

Différence entre l'utilisation de subjects (gauche) et d'observables ( droite )

On execute l'observable 1 seule fois

On execute l'observable 2 seule fois

    source : 1
    subject subscribe 1, val : 1
    subject subscribe 2, val : 1
    source : 2
    subject subscribe 1, val : 2
    subject subscribe 2, val : 2
    source : 3
    subject subscribe 1, val : 3
    subject subscribe 2, val : 3
  source : 1
  obs subscribe 1 1
  source : 2
  obs subscribe 1 2
  source : 3
  obs subscribe 1 3
  source : 1
  obs subscribe 2 1
  source : 2
  obs subscribe 2 2
  source : 3
  obs subscribe 2 3

Affichage dans la console des exemples précédants.

l'Observable est exécuté 1 fois

l'Observable est exécuté 2 fois

Input / Ouput

On peut transférer et récupérer des données d'un component

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
})
export class ExampleComponent implements OnInit {
  @Input() data: string;
  @Output() dataEvent = new EventEmitter<string>();
  
  
  ngOnInit(): void {
  	console.log('input : ', this.data);
  }

  emitValue(value: string) {
    this.dataEvent.emit(value);
  }
  

}
<app-example [data]="'value or variable'" 
(dataEvent)="doSomething($event)">
</app-example>

La fonction 'doSomething()' sera appelé quand un événenement est émit.

Text

La donnée passé par la variable 'data' est récupéré pendant la construction du component.

Exemple en groupe

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10. o

Un flux d’événements angular asynchrone auquel on peut s'inscrire

11.​

12.

13.

14.

15.

16.

17.

Objectifs 

Découvrir les features et concepts du framework avec un example pratique sur une api existante.

Aller sur stackblitz

Copier en local

  1. forker le stackblitz

  2. connecter à un nouveau github

  3. cloner en local

Module Core

Créer le module Core avec un service d'authentification qui permet de faire des appels à l'api pour se connecter, s'inscrire et appeler la route ping pour récupérer l'utilisateur connecté 

Module Auth

- Créer le module "Auth"

- Créer une page de login avec une route dédiée 

- Créer un formulaire de login en utilisation l'approche Reactive avec un FormGroup

- Connecter le formulaire à l'api via le service d'auth

Session

- Créer le service session qui permet de stocker en localstorage le token renvoyé lors du login

Dashboard

- Créer le module "dash"

- Créer une page d'accueil vers laquelle on est redirigé après login

Lazy loading

- Charger dynamiquement les modules Auth et Dash en créant les fichiers auth-routing.module.ts et dash-routing.module.ts pour gérer localement les routes

Guard

- créer un guard qui faire une requête à la route ping ( via authService ) et autoriser ou non l'accès en fonction du statut de la réponse de la requête

- assigner ce guard à la route '/dash'

Intercepteur

- créer un intercepteur qui va ajouter le token ( jwt ) stocké dans la session à chaque requête sous la forme :

Authorization: `Bearer ${token}`

- assigner ce guard à la route '/dash'

Inscription

- créer une page 'signup'

- créer un formulaire pour l'inscription avec les champs : 'first_name', 'last_name', 'email', 'password'

- mettre de la validation sur les champs email et mot de passe ( + de 6 caractères )

Redirection

- rediriger l'origine vers signin

- faire une page 404 pour les autres cas

Utilisateur connecté

- récupérer l'utilisateur connecté via le guard quand le statut est 200 et le stocker dans auth.service.ts

Déconnexion

- ajouter un bouton déconnexion qui fait retourner l'utilisateur vers la page de login

- au moment de la déconnexion vider le localstorage et réinitialiser service auth

Matching

- récupérer les utilisateurs qui matchent avec le profil connecté à l'aide d'un service 'match'

- afficher les données des matchs ( nom, prénom, tags d'apprentissage ) dans des cartes

Page profil

- créer une page profil avec un formulaire ( 'first_name', 'last_name', 'tags' ), utiliser un select multi Material

- connecter le formulaire à l'api en créant un service 'profile'

- récupérer les tags pour le select avec l'api dédié à un auto-complete en créant un service 'tag'

Refacto du formulaire

- créer un module "shared"

- refactorer les formulaires en mettant en commun les champs : "prenom", "nom", "email", "mot de passe" avec des components dédiés en passant le formGroup en Input des components enfants, dans le module "shared"

Admin

- créer un module admin et le charger dynamiquement (lazy loading) sur la route 'admin'

- créer une page admin accessible uniquement en étant connecté

- sur la route 'admin' passer en data : { admin : true }

- mettre à jour le guard pour permettre l'accès aux routes où est spécifié la data { admin :true } uniquement si l'utilisateur a le role 'ROLE_ADMIN' ( transmis via l'api ) 

API

login_check (post) : { email, password }
=> { token }

signup (post) : { email, password, first_name, last_name }
=> User

Connexion

Inscription

URL : https://api-teaching.wecolearn.com/api/

Vérifier la connexion et récupérer les données utilisateurs

ping (get) : => User

tag/find (get) : => Tag[]

user/matchs (post) : { max? }
=> User[]

Récupérer des tags ( pour un auto-complete )

Récupérer les profils qui matchent

profile (patch) : { first_name, last_name, tags } => User

Modifier les données du profil

admin/tags (get) : => Tag[]

Récupérer tous les tags ( admin )

Angular

By samuel eyre