2.Présentation d'Angular

Angular est un framework frontend survitaminé permettant de réaliser simplement des interfaces web riches et dynamiques.

Plus qu’une bibliothèque, la solution Angular propose un cadriciel complent s’articulant autour de solides patrons de conceptions.

Si son nom est commun à celui de son grand-frère Angular.JS, cette nouvelle version n’a a beaucoup mûrie est facilite grandement la vie des développeurs web.

En bref

A quoi sert Angular ?

  • Structurer une application client
    • Architecture MV* > la page HTML / PHP et les cinquantes fichiers JS
    • Plus facile à maintenir
    • Notion de design modulaire
  • Faciliter le développement d’interface client riche
    • Templates HTML Dynamiques
    • Asynchronisme par défaut (Observable / Promise)
    • Liaison des données simples
  • Notion de Single Page App
    • Une page chargée gérant en interne le routage, les autorisations…
    • Possibilité de lazy loading de module (certaines parties ne sont chargées que quand nécessaire)

Angular.JS / Angular 2-8

  • Angular.JS : version 1, encore maintenue
  • Angular 2 : La nouvelle version, toute propre
  • Angular 4 : Passage au semantic versioning
  • Angular 8 : Version actuelle sortie fin mai 2019

Exemple : Numéro de version 1. 2. 3

  1. Version MAJEUR quand l’api fait des changement incompatible avec la version précédente
  2. Version MINEURE quand des fonctionnalités sont ajoutées et conserve la compatibilité avec la version précédente
  3. Version PATCH pour les corrections de bugs.

Typescript

Lien vers la documentation

  • Meta-langage Javascript
    • Ajoute des fonctionnalités au JS (en plus d’être compatible avec les prochaines encore non implémentées)
    • Propose une structure d’utilisation plus strict
    • Est compilé en JS
  • OpenSource
    • Maintenu par Microsoft
    • Utilisé par Ionic, Angular, Aurelia
  • Notion de typage
    • Contrôle des erreurs à la compilation
    • Les IDE permettent ainsi l’auto-complétion
    • Documentation plus lisible
  • Interfaces, enums, décorateurs…
    • Syntaxe Objet
    • Décorateurs : métadonnées permettant d’intéragir avec le code (lecture / écriture) à la compilation
  • ! Attention ! : ajoute des étapes nécessaires dans la transformation du code source
    • Compilation du code TS => JS
    • Utilisation de fichiers de correspondance TS <=> JS pour le deboggage (fichiers map)

Exemple de classe Typescript

import {Injectable} from "@angular/core";
import {UserSegment} from "../model/user-segment";
import {Story} from "../../story/model/story";
import {Recorder} from "./recorder";
import {RecordFileManager} from "./record-file-manager.service";
import {UserSegmentService} from "./user-segment.service";

@Injectable()
/**
 * Record Service
 * Abstraction of story recording: the service manages the data story
 * and the files persistence.
 */
export class RecordService {

  private _recorder: Recorder;
  private _data: Array<Blob>;

  constructor(
    private userSegmentService: UserSegmentService,
    private recordFileManager: RecordFileManager
  ) {
    this._recorder = new Recorder();
  }

  public get data() {
    return this.data;
  }

  public async newSession(story:Story) {
    let currentUserSegment: UserSegment = await this.userSegmentService.create(story.segments[0]);
    this._data = [];
  }
  // ...
}

Une architecture MV*

Structure d’Angular

Composant

  • Un composant == une brique de Lego avec son propre comportement et son propre style
    • Un composant contrôle une partie de l’écran appelé une vue
    • Exemple : Une map Google, une vidéo youtube, un lecteur audio…
  • Les composants peuvent s’imbriquer les uns dans les autres
    • Ex. : Un lecteur audio imbrique des boutons
  • Chaque composant a :
    • un identifiant <my-id></my-id>
    • des attributs <my-id attrib="smthg"></my-id
    • Ex. : Lecteur Audio: <audio></audio>

Exemple

<audio controls>
  <source src="horse.ogg" type="audio/ogg">
  <source src="horse.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>

Dans l’exemple ci-dessus, le composant audio a pour identifiant audio. La déclaration de son attribut controls active la présence des boutons de contrôle. Les sous-composants source décrivent les liens vers les fichiers audios.

Dans le cadre d’une application Angular, un composant est la synthèse entre :

  • Un template HTML
  • Une feuille de style CSS/SCSS
  • Un contrôleur .TS/.JS pouvant être lié à plusieurs services.

Imbrication de composants

Directives

L’affichage d’une page HTML avec Angular est réalisé de manière dynamique. Cela signifie que le DOM est construit en respectant les instructions décrites par les directives. Une directive est définie par le décorateur @Directive. Un composant décrit par @Component est une directive avec un template.

Il existe deux types de directives : structurelles et attributs

Directives structurelles

Elles modifient la structure d’un template HTML en ajoutant, retirant, modifiant des éléments du DOM.

Par exemple :

<li *ngFor="let story of stories"></li>
<story-detail *ngIf="selectedStory"></story-detail>
  • *ngFor génère autant d’éléments <li> qu’il y a d’objets dans le tableau stories
  • *ngIf affichera la composant <story-detail> uniquement quand la valeur de selectedStory existera

Ces directives sont dynamiques. Par défaut, toute modification des valeurs référentes (stories et selectedStory) entraîne une reconstruction du DOM et un rafraîchissement de la vue.

Directives attributs

Elles modifient l’apparence ou le comportement d’éléments existant. Elles ressemblent à des attributs HTML.

Une directive attribut ne correspond pas aux attributs déjà existants d’un composant. Au contraire, elle permet d’ajouter des mécaniques supplémentaires.

Les directives ngSwitch, ngStyle et ngClass modifient l’aspect d’éléments du DOM et des composants.

Ce type de directive est reconnaissable par la présence des marqueurs de liaison de données : [] |& ()

Par exemple :

<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div>

Services

  • Factorisation de code métier
    • Toute code présent et dupliqué entre deux composants doit être centralisé dans un service.
    • La taille des contrôleur doit être la plus petite possible.
    • => Externalisation le plus possible du comportement des contrôleurs dans un service
  • Injectable : une instance de service est utilisable par les composants et les autres services.
    • Cette dépendance est résolue par le système d’injection de dépendance d’Angular
    • Une instance de service est commune pour tous les éléments dépendants du même niveau hierarchique d’injection (voir la section Injection de dépendance)
      • -> Utilisable pour transmettre des données entre composants
  • Exemples de services
    • Service d’authentification
    • Persistence des données
    • Calculs de taxes

Modèles

  • Structurent des données utilisées par l’application
  • Ne contiennent pas de code métier

Une classe Typescript / JS (introduite en ES2015)

  • Approche objet
  • Sucre syntaxique uniquement

Exemple

import {Segment} from "./chapter";
export class Story {
  private _title:string;
  private _chapters:Array<Chapter>;

  constructor(title?: string, chapters?: Array<Chapter>) {
    this._title = title;
    this._chapters = [];
  }

  get title(): string {
    return this._title;
  }

  set title(value: string) {
    this._title = value;
  }
  // ...

Une interface

Les interfaces Typescript permettent de décrire le type des attributs d’un Objet javascript. Son rôle principal est la description.

À la différence d’une classe, l’interface ne propose par de comportement pour les objets.

Une interface peut être utilisée pour décrire un objet javascript construit dynamiquement ou obtenu par déserialisation par exemple.

Exemple

import {iChapter} from "./ichapter";

export interface iStory {
  title: string;
  chapters: Array<iChapter>;
}

Pipes

  • Ils permettent de transformer dynamiquement les valeurs affichées dans un template
  • Ils sont chaînables
  price | currency:'USD':true

Affiche le prix 42.33 sous la forme $42.33 cf. doc.

Références

Les composants

Un composant web est la synthèse

  • d’un comportement (component): TS/JS
  • d’une mise en forme (template): HTML
  • d’une feuille de style : css/scss

Le contrôleur TS / JS, component

  • Liaison entre les services et l’interface graphique
  • Code uniquement du comportement d’une page
  • Exposition de variables et de fonctions pour la page

Code type d’un contrôleur

import { Component } from '@angular/core';

@Component({
  selector: 'pause-btn',
  templateUrl: 'pause-btn.html',
  styleUrl: 'pause-btn.scss' 
})
export class PauseBtn {
  protected _paused: boolean;
  
  constructor() {
    this._paused = false;
  }

  get paused():boolean {
    return this._paused;
  }

  pause():boolean {
    this._paused = true;
    return this.paused;
  }
}

Le template HTML

Un template HTML décrit la structure du DOM du composant. Il peut être composé des balises HTML standards, de composants, directives et il peut utiliser des pipes pour modifier l’affichage de certaines données.

Ainsi, un template HTML Angular est lié de manière dynamique à des variables et des fonctions issues du contrôleur du composant auquel il est associé.

Les Expressions

Pour cela, Angular propose des expressions, encadrées par des {{ myExpression }}. Ces expressions permettent d’injecter de manière dynamique le contenu d’une variable au sein d’une balise HTML.

Par exemple, <h1>{{title | uppercase}}</h1> le contenu de la variable title décrite dans le contrôleur Typescript associé, sera insérée dans la balise <h1> et sera mise en majuscule automatiquement.

Toute modification de la variable title entraînera le rafraîchissement de la vue.

Les attributs HTML

Template contrôleur

Pour associer la valeur d’un attribut HTML à une variable ou une fonction, Angular propose la syntaxe suivante.

<img [src]="getImageSrc()" />

L’attribut src de la balise <img> est alors associé au résultat de la fonction getImageSrc().

Ce résultat est dynamique : si la fonction retourne à l’instant t+1 un résultat différent de l’instant t, alors la valeur de l’attribut src est modifié.

Ainsi, grâce à cette liaison de donnée, la source de l’image peut être modifié dynamiquement.

Template contrôleur

Pour réaliser une liaison de données depuis le template vers le contrôleur, Angular propose l’utilisation des parenthèses ().

<img src="..." (click)="imgAction()"/>

Lorsque l’utilisateur cliquera sur l’image, il entraînera le déclenchement de la fonction imgAction() décrite par le contrôleur typescript.

Conclusion

  • Template : construit de manière dynamique selon les composants, directives et pipes
  • Liaisons de données :
    • Template contrôleur : <a [href]="myVar">link</a>
    • Template contrôleur : <a (click)="myFunction()>link<./a>
    • Template contrôleur : <input [(ngModel)]="name" />

La feuille de style

Les feuilles de style d’une application Angular respectent la même notion de construction hiérarchique.

Le composant racine peut posséder une feuille de style initiale définissant un style qui sera utilisé pour lui et tous ses sous-composants.

Chaque composant d’un niveau hiérarchique n hérite des guides de style de ses composants parents n-m pour tout m > 1. Cependant, le style qu’il définira à son niveau n ne sera accessible que pour lui et ses sous-composants n+m pour tout m >= 1.

Exemple

button-red {
    background-color: color($colors, red);

    div {
      background-color: yellow;
    }
}
  • Un composant hérite du style de ses composants parents
  • Sont style ne s’applique qu’au composant et à ses sous-composants
  • Peut être écrite en SCSS
    • Utilisation de variables, mixins…

Les services

D’après Angular, un service est un composant tiers contenant du code métier. Cette définition floue laisse libre au développeur l’interprétation du terme service.

Angular propose ainsi au développeur une mécanique d’injection de dépendance hiérarchique. Un service peut être injecté grâce à ce système. On identifiera ainsi un service injectable avec le décorateur @Injectable().

L’injection de services tiers se fait par le constructeur. Lorsque notre service est instancié par l’application, ses dépendances sont résolues.

Structure d’un service

@Injectable()
export class StoryService {

  constructor(
    public logger: Logger
  ) {}

  
}

La classe StoryService est ici considérée comme injectable.

Le constructeur de la classe décrit la dépendance du service Logger. La précision public est une facilité syntaxique pour décrire que ce paramètre de construction est également un attribut de la classe, accessible par this.logger.

À l’instanciation de la classe StoryService, la dépendance sur service Logger récupérera l’instance associé à ce type le plus proche.

Injection de la dépendance

Le terme plus proche désigne le processus suivant.

En considérant l’inclusion par modules, chaque service appartient à un module, le module racine d’une application étant le tout premier niveau.

Chaque module peut inclure plusieurs modules et ce récursivement. Ainsi, lors de l’injection de dépendance, le système d’injection cherche à chaque niveau hiérarchique de module si une intance de Logger a été précisée, en commençant par le niveau du module injecteur et en remontant niveau par niveau les modules parents.

Lorsqu’une instance du service a été trouvé à un étae, celle-ci est associée à l’attribut du service instancié, logger ici. Le cas contraire, le système remonte d’un étage supplémentaire, jusqu’à trouver une instance.

Conclusion

  • Un service : potentiellement injectable
  • Résolution hiérarchique de dépendance

Les modules

Un module désigne un ensemble indépendant de fonctionnalités d’une application. Un module peut contenir des composants, services, pipes, styles, templates et d’autres modules.

Une application Angular est un module, qui peut inclure d’autres sous-modules.

Les modules permettent d’organiser une application en la structurant en briques indépendantes facilement réutilisables et testables.

Politique d’isolation fonctionnel

Une application Angular est ainsi l’articulation d’un ensemble de modules, dépendants les uns des autres selon une structure hiérarchique.

Structure d’un module

Un module est décrit par le décorateur @NgModule(). Celui-ci possède plusieurs options de configuration. Voir la documentation de NgModule

@NgModule({
  declarations: [ // Importation des pages
      MyApp,
      AboutPage,
      ContactPage,
      HomePage
  ],
  imports: [ // Imports d'autres modules
    BrowserModule,
    HttpModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp], //
  entryComponents: [ // Component factory, est nécessaire en cas d'eager loading
      MyApp,
      AboutPage,
      ContactPage,
      HomePage
  ],
  providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

Sur cette exemple, on distingue plusieurs options de configuration qui permettent de paramétrer un module.

declarations
Déclare une liste de directives / pipes qui appartiennent au module. Cela ne les instancie pas.

providers
Définit un ensemble d’objets injectables qui seront utilisables par l’injecteur de ce module. Cette option sera utilisée par l’injecteur pour créer les instances de chaque service mentionné et génèrera un singleton propre au module.

exports
Chaque module peut exporter ce qu’il contient : directives, composants, pipes, services. Lorsque ce module sera importé par un module tiers, seuls les éléments exposés ici seront disponibles.

imports
L’options permet de spécifier une liste de modules qui seront importés afin d’importer les éléments publiques des modules (directives, composants, pipes, services).

bootstrap
Définit les composants qui devront être amorcés (bootstrappés) lorsque ce module le sera. Les composants mentionnés ici seront automatiquement ajoutés à entryComponents.

entryComponents
Définit une liste de composants qui devront être compilés quand le module sera défini.

La gestion des dépendances

L’injection d’une dépendance de service recherche l’instance de service cible la plus proche.

Cette instanciation peut être réalisée par l’injecteur au niveau du module, d’un composant ou d’un service.

Injection dans un module

Comme décrit plus haut, l’option providers du décorateur @NgModule() permet de déclarer l’instanciation d’un service de le rendre disponible pour tous les éléments du modules (services, pipes, composants).

Injection dans un composant / Service

Il est également possible de déclarer l’instanciation d’un service dans un composant, service, pipe. Ces décorateurs possèdent également une option providers.

Un service instancié au niveau de ces éléments aura la priorité la plus importante lorsque l’injecteur résoudra les dépendances.

Conclusion

  • Organisation : Les modules permettent d’organiser structurellement une application Angular
  • Encapsulation : Chaque modules peut importer plusieurs éléments et rendre public ce qu’il contient
  • Injection : La résolution de dépendance est résolue de manière hiérarchique (au plus proche). Chaque élément peut déclarer l’instanciation d’une instance de servie.

Conclusion générale

Cette présentation succinte présente les bases d’Angular. La documentation officielle est bien illustrée et présente plusieurs catégories bien construites :