Les spas sont devenus à la mode comme l'antithèse des sites Web rendus côté serveur. Il y a des avantages et des inconvénients, comme toujours, mais un spa peut créer une expérience utilisateur fluide qui est difficile à reproduire sans une telle approche. Cela fait partie de l'entreprise des applications Web sur le territoire qui était traditionnellement occupé par les applications de bureau. Les applications Web sont généralement critiquées pour être lentes par rapport aux applications de bureau, mais les progrès majeurs des technologies Web (notamment Nodejs et le moteur V8 de Google, ainsi que des frameworks comme Angular et ReactJS) ainsi que l'accès dominant à une puissance informatique sans précédent signifie que cela signifie que cela signifie que cela moins de problème qu'auparavant.
Spas fonctionne en chargeant une seule page HTML sur le client, puis en mettant dynamiquement le contenu (et le DOM) - au lieu de récupérer une page HTML mise à jour du serveur chaque fois que l'utilisateur clique n'importe où.
NodeJS
et npm
en suivant les instructions appropriées ici.TypeScript
à partir d'ici.npm install -g @angular/cli
ng new my-project
cd my-project && ng serve
localhost:4200
dans votre navigateur Web. Si vous ouvrez my-project/src/app/app.component.ts
vous devez être accueilli avec quelque chose comme le suivant:
/** app.component.ts */
import { Component } from '@angular/core' ; // #1
@ Component ( { // #2
selector : 'app-root' , // #3
templateUrl : './app.component.html' , // #4
styleUrls : [ './app.component.css' ] // #5
} )
export class AppComponent {
title = 'app' ; // #6
}
Décomposons un peu cela:
Component
à partir du module @angular
situé dans le dossier node_modules
.@Component
marque une classe en tant que composant angulaire. Le décorateur est paramétré avec un objet Options, dont nous n'utiliserons que quelques paramètres.selector
définit ce que seront les balises HTML pour ce composant - par exemple, celle-ci sera injectée dans HTML à l'aide de <app-root> … </app-root>
.templateUrl
prend un argument de chaîne qui pointe vers le fichier HTML qui sert de partie de vue du composant. Vous pouvez également utiliser le paramètre template
pour écrire HTML directement, au lieu de pointer d'un fichier. Ceci n'est généralement pas recommandé, sauf si la partie de la vue est simplement une ligne ou deux.styleUrls
prend une liste de chaînes, où chaque chaîne est un chemin d'accès à un fichier CSS.title
peut être référencé à partir d' app.component.html
. Accédez à app.component.html
. Il s'agit du point d'entrée de notre application, et le HTML le plus haut niveau que vous devriez modifier. Tout sera rendu dans ce contexte.
Nous allons maintenant supprimer le contenu de ce fichier et le remplacer par ce qui suit:
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< div class =" score-card " >
< span > Team 1 </ span >
< span > 0 : 0 </ span >
< span > Team 2 </ span >
</ div >
</ div >
</ div >
Retournez à votre navigateur Web et vous devriez voir que la page a été mise à jour automatiquement. Super.
Visuellement vous serez probablement déçu à ce stade, mais c'est un travail en cours.
Angular Material est une bibliothèque maintenue par une équipe de Google afin de fournir des composants de conception de matériaux faciles à utiliser pour les applications angulaires. Le style de ces composants peut être modifié au contenu de votre cœur, mais il offre également un moyen facile de mettre à niveau l'apparence d'une application prototype avec un minimum d'effort.
Installons les bibliothèques requises avec npm install --save @angular/material @angular/cdk @angular/animations
. Cela télécharge les fichiers requis dans votre dossier node_modules
et met à jour le fichier packages.json
de manière appropriée.
Vous devrez également dire à Angular de charger les composants pertinents. Vous devrez faire un peu sur Google sur la façon dont cela fonctionne (il s'agit d'un tutoriel de bande 101 conduits), mais en gros, il vous suffit de modifier le fichier app.modules.ts
pour inclure les modules dont vous avez besoin.
Tout d'abord, ajoutez les animations comme ainsi:
import { BrowserAnimationsModule } from '@angular/platform-browser/animations' ;
@ NgModule ( {
...
imports : [ BrowserAnimationsModule ] ,
...
} )
Puis ajoutez l'importation des modules dont nous avons besoin:
import { MatButtonModule , MatCardModule } from '@angular/material' ;
@ NgModule ( {
...
imports : [ MatButtonModule , MatCardModule ] ,
...
} )
Sachez que vous devrez importer les composants que vous utilisez dans votre application. La raison de ne pas inclure toute la bibliothèque est d'aider webpack
à effectuer des tremblements d'arborescence, ce qui implique essentiellement de laisser des pièces de code inutilisées lorsque vous regroupant tout notre code dans quelques fichiers .js
a minimisé.
Une fois cela fait, nous aurons accès à un tas de composants prédéfinis, et vous pouvez simplement les importer selon les besoins. Il convient également de noter que les thèmes prédéfinis sont faciles à utiliser.
En angulaire, les composants sont modulaires. Chaque composant a ses propres fichiers CSS, qui ne s'appliquent qu'à cette vue spécifique. La seule exception est le fichier styles.css
trouvé dans le répertoire src
qui s'applique à l'échelle mondiale.
Le style n'est pas un objectif de ce tutoriel, nous allons donc essayer de l'éviter autant que possible à l'avenir. Afin de se sentir légèrement mieux dans ce que nous faisons, voici quelques CSS simples à copier et à coller dans le fichier styles.css
(une meilleure pratique serait d'ajouter des bits pertinents aux fichiers CSS des composants appropriés, pour référence future):
// Outer wrapper div
. container {
text-align : center;
}
// Wrapper for the score cards
. score-card-list {
display : flex;
flex-direction : column;
align-items : center;
}
// Each score card
. score-card {
width : 550 px ;
display : flex !important ;
}
// Home team score
. home {
flex : 1 ;
text-align : right;
}
// Away team score
. away {
flex : 1 ;
text-align : left;
}
// Score section of score card
. score {
width : 100 px ;
}
Mettez également à jour votre HTML pour inclure les classes:
...
< mat-card class =" score-card " >
< span class =" home " > Team 1 </ span >
< span class =" score " > 0 : 0 </ span >
< span class =" away " > Team 2 </ span >
</ mat-card >
...
Si vous êtes confus à propos de flex
, je recommande fortement de consulter Flexbox Froggy.
L'un des avantages de l'utilisation d'un cadre comme Angular est que nous pouvons implémenter une logique de programmation directement dans le HTML. Dans notre cas, pour les boucles rendront la vie beaucoup plus facile.
Essayez ce qui suit:
...
< mat-card class =" score-card " *ngFor =" let team of ['Arsenal', 'Liverpool', 'Tottenham']; let i=index " >
< span class =" home " > {{ team }} </ span >
< span class =" score " > 0 : 0 </ span >
< span class =" away " > Team {{ i+1 }} </ span >
</ mat-card >
...
Cela introduit une gamme de nouveaux concepts et devrait produire quelque chose comme ce qui suit:
Concepts à noter:
{{ 'Some text = ' + a_number }}
Les services sont utilisés afin de résumer l'accès aux données loin des composants. Cela permet aux composants de rester maigres et de se concentrer sur la prise en charge de la vue. Les tests unitaires et la maintenance du code sont simplifiés en maintenant cette séparation de responsabilité.
Pour générer un service à l'aide de la CLI, vous pouvez utiliser ng generate service football-data
(ou ng gs football-data
pour les personnes à court de temps). Cela créera deux nouveaux fichiers dans votre répertoire project-dir/src/app
: football-data.service.ts
et football-data.service.spec.ts
. Cela suit la convention angulaire par laquelle les fichiers de test obtiennent l'extension .spec.ts
. Bien que les tests soient utiles et importants, il tombe en dehors de la portée immédiate de ce tutoriel afin que vous puissiez l'ignorer pour le moment (désolé, @Cornel).
Ajoutez les lignes suivantes à football-data.service.ts
:
import { Injectable } from '@angular/core' ;
import { HttpHeaders } from '@angular/common/http' ;
@ Injectable ( ) // Designate this class as an injectable service
export class FootballDataService {
// Token for accessing data on football-data.org (see: https://www.football-data.org/client/register)
HEADERS = new HttpHeaders ( { 'X-Auth-Token' : 'dc25ff8a05123411sadgvde5bb16lklnmc7' } ) ;
// Convenience constant
COMPETITION_URL = 'http://api.football-data.org/v1/competitions/' ;
// ID for the Premier League
PL_ID = 445 ;
constructor ( ) { }
}
Les services sont annotés avec @Injectable
, qui indique au compilateur que les instances du service peuvent être injectées en composants. Vous devez également spécifier un fournisseur pour chaque service. Le fournisseur fournit effectivement un singleton au niveau spécifié.
Par exemple, vous pouvez ajouter votre service au tableau des fournisseurs dans app.module.ts
, qui se traduira par une instance singleton du service exposé dans toute l'application. Sinon, vous pouvez l'ajouter à la liste des fournisseurs d'un composant, ce qui entraînera une instance de singleton à la disposition de ce composant et de tous les composants dans son contexte.
AVERTISSEMENT: il devient facile de créer des dépendances circulaires entre les services une fois que votre application a augmenté. Soyez attentif à cela. Si (quand) vous obtenez un message d'erreur qui ressemble à quelque chose Cannot resolve all parameters for MyDataService(?)
, Il est probablement lié à des problèmes de dépendance circulaire.
Nous utiliserons gratuitement l'API fantastique sur Football-Data.org. Vous devrez obtenir votre propre clé API, mais c'est facile à faire - suivez simplement les instructions sur le site. Il y a plus de documentation approfondie disponible, mais 99% de ce dont vous avez besoin peut être vu dans les petits exemples énumérés ici.
Pour cet exemple, tout ce que nous voulons vraiment faire, c'est récupérer les informations de jeu pour tous les jeux dans la manche actuelle (aka "Game Week" ou "Match Day"). Le point de terminaison /v1/competitions/{id}/fixtures
renvoie ces informations, mais pour tous les derniers tours de la saison en cours. Afin d'obtenir les informations pour un seul tour, nous devons définir le paramètre matchday
, par exemple /v1/competitions/{id}/fixtures?matchday=14
.
Afin d'obtenir le jour du match en cours, nous pouvons demander le tableau de la ligue, car il revient pour le jour du match en cours par défaut.
Tout d'abord, nous devons injecter le service HttpClient
dans notre FootballDataService
afin d'utiliser les fonctions HTTP d'Angular:
import { HttpClient , HttpHeaders } from '@angular/common/http' ;
...
constructor ( private http : HttpClient ) { }
. . .
Important : l'ajout d'une variable privée au constructeur d'un service ou d'un composant angulaire, ainsi que de la déclaration de type TypeScript spécifique, est une information suffisante pour que la magie noire d'Angular fonctionne. Le compilateur injectera désormais l'instance appropriée dans ce service, vous y avez donc accès.
Ajoutons une fonction pour récupérer le tableau de la ligue (et le jour de match actuel) du serveur:
...
getTable ( ) {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } ) ;
}
...
TypeScript vous aidera avec cela, mais la signature de la méthode pour la méthode http.get
d'Angular ressemble à la suivante:
/**
* Construct a GET request which interprets the body as JSON and returns it.
*
* @return an `Observable` of the body as an `Object`.
*/
get ( url : string , options ?: {
headers ?: HttpHeaders | {
[ header : string ] : string | string [ ] ;
} ;
observe?: 'body' ;
params?: HttpParams | {
[ param : string ] : string | string [ ] ;
} ;
reportProgress?: boolean ;
responseType?: 'json' ;
withCredentials?: boolean ;
} ) : Observable < Object > ;
Les points d'interrogation indiquent que headers
de paramètres, observe
, params
, reportProgress
, responseType
et withCredentials
sont tous facultatifs dans le cadre de l'objet facultatif pour options
. Nous ne ferons passer que des valeurs pour l' url
et options.headers
.
Vous vous demandez peut-être quelle est la fonction getTable()
que nous venons de créer de retour. Eh bien, il renvoie un flux Observable
. Les S Observable
sont essentiellement, comme le dit Luuk Gruijs, "des collections paresseuses de plusieurs valeurs au fil du temps". Ce qui semble fou, mais qui est en fait assez simple.
En bref, un flux Observable
est considéré comme "froid" jusqu'à ce qu'il soit abonné - c'est-à-dire que la variable n'est chargée que paresseusement une fois que sa sortie est utilisée. Une fois que le flux est "chaud", il mettra à jour tous les abonnés chaque fois que sa valeur change. Il s'agit d'une alternative à l'utilisation Promise
pour gérer les situations asynchrones en JavaScript.
Dans ce cas, la demande GET
ne sera licenciée qu'une fois la variable Observable
abonnée car l'interface de repos ne renverra jamais une seule valeur par appel.
Nous pouvons utiliser la puissance du typage statique dans TypeScript pour rendre cela plus explicite:
...
import { Observable } from 'rxjs/Observable' ;
...
getTable ( ) : Observable < any > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } ) ;
}
...
En fait, puisque la documentation de football-data.org nous dit exactement à quoi attendre de l'appel de repos, nous pouvons aller plus loin et modéliser l'objet également, dans src/app/models/leagueTable.ts
:
import { Team } from './team' ;
export class LeagueTable {
leagueCaption : string ;
matchday : number ;
standing : Team [ ] ;
}
Et dans src/app/models/team.ts
:
export class Team {
teamName : string ;
crestURI : string ;
position : number ;
points : number ;
playedGames : number ;
home : {
goals : number ;
goalsAgainst : number ;
wins : number ;
draws : number ;
losses : number ;
} ;
away : {
goals : number ;
goalsAgainst : number ;
wins : number ;
draws : number ;
losses : number ;
} ;
draws : number ;
goalDifference : number ;
goals : number ;
goalsAgainst : number ;
losses : number ;
wins : number ;
}
Qui nous permet de mettre à jour le football-data.service.ts
à:
import 'rxjs/add/operator/map' ;
import { LeagueTable } from './models/leagueTable' ;
...
getTable ( ) : Observable < LeagueTable > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } )
. map ( res => res as LeagueTable ) ;
}
...
Cela nous aidera à maintenir notre santé mentale en minimisant le modèle mental dont nous devons nous tenir au courant tout en travaillant avec des objets complexes, car l'IDE peut nous guider.
Remarque latérale: le mot clé as
indique simplement à TypeScript de nous faire confiance au type de l'objet, plutôt que d'essayer de le comprendre via une sorte d'inspection. Dangereux mais utile, comme les choses la plupart des choses intéressantes.
D'accord, rendez-vous à src/app/app.component.ts
et ajoutez les lignes suivantes afin d'injecter le FootballDataService
dans le composant:
import { FootballDataService } from './football-data.service' ;
...
export class AppComponent {
title = 'app' ;
constructor ( private footballData : FootballDataService ) { }
}
Maintenant, nous allons également ajouter une méthode ngOnInit
au composant. Il s'agit d'un crochet de cycle de vie standard fourni par Angular qui tire après que toutes les propriétés liées aux données d'un composant sont initialisées. Il tire essentiellement sur l'initialisation de l'objet, mais légèrement plus tard que la méthode constructor
qui tire avant que toutes les entrées et sorties du composant aient été enregistrées.
Une règle de base commune consiste à toujours placer du code que vous souhaitez invoquer lors de l'initialisation dans cette méthode ngOnInit
spéciale, plutôt que sur le constructeur. Ajoutez-le à un composant comme tel:
import { Component , OnInit } from '@angular/core' ;
...
export class AppComponent implements OnInit {
...
constructor ( private footballData : FootballDataService ) { }
ngOnInit ( ) {
// Code you want to invoke on initialisation goes here
}
...
Dans notre cas, nous voulons charger le tableau de la ligue, afin que nous puissions ajouter quelque chose comme ceci:
...
ngOnInit ( ) {
// Load league table from REST service
this . footballData . getTable ( )
. subscribe (
data => console . log ( data ) ,
error => console . log ( error )
) ;
}
...
Si vous ouvrez la console de votre navigateur Web, vous devriez voir quelque chose comme ceci:
Super. Nous avons maintenant beaucoup de données intéressantes avec lesquelles nous pourrions faire quelque chose à l'avenir (notamment des liens directs vers les images de Club Crest), mais pour l'instant, nous sommes vraiment intéressés par la journée actuelle.
Ajoutons maintenant une autre fonction à notre service de données afin d'obtenir les informations de la série actuelle de luminaires:
...
import { GameWeek } from './models/gameWeek' ;
...
getFixtures ( matchDay : number ) : Observable < GameWeek > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/fixtures?matchday=' + matchDay , { headers : this . HEADERS } )
. map ( res => res as GameWeek ) ;
}
...
avec GameWeek
et Fixture
défini comme suit:
// src/app/models/gameWeek.ts
import { Fixture } from './fixture'
export class GameWeek {
count : number ;
fixtures : Fixture [ ] ;
}
// src/app/models/fixture.ts
export class Fixture {
awayTeamName : string ;
date : string ;
homeTeamName : string ;
matchday : number ;
result : {
goalsAwayTeam : number ;
goalsHomeTeam : number ;
halfTime : {
goalsAwayTeam : number ;
goalsHomeTeam : number ;
}
} ;
status : 'SCHEDULED' | 'TIMED' | 'POSTPONED' | 'IN_PLAY' | 'CANCELED' | 'FINISHED' ;
_links : {
awayTeam : { href : string ; } ;
competition : { href : string ; } ;
homeTeam : { href : string ; } ;
self : { href : string ; } ;
} ;
}
Avec nos connaissances de la journée de match nouvellement acquises, nous pouvons demander au serveur de repos des informations sur la série actuelle des luminaires. Cependant, nous devons attendre que le premier appel de repos se termine avant de faire la seconde. Un peu de refactorisation signifie que nous pouvons le faire dans le rappel assez facilement:
import { Component , OnInit } from '@angular/core' ;
import { FootballDataService } from './football-data.service' ;
import { LeagueTable } from './models/leagueTable' ;
@ Component ( {
selector : 'app-root' ,
templateUrl : './app.component.html' ,
styleUrls : [ './app.component.css' ] ,
providers : [ FootballDataService ]
} )
export class AppComponent implements OnInit {
title = 'app' ;
table : LeagueTable ;
gameweek : GameWeek ;
constructor ( private footballData : FootballDataService ) { }
ngOnInit ( ) {
this . getTable ( ) ;
}
getTable ( ) {
this . footballData . getTable ( )
. subscribe (
data => {
this . table = data ; // Note that we store the data locally
this . getFixtures ( data . matchday ) ; // Call this function only after receiving data from the server
} ,
error => console . log ( error )
) ;
}
getFixtures ( matchDay : number ) {
this . footballData . getFixtures ( matchDay )
. subscribe (
data => this . gameweek = data , // Again, save locally
error => console . log ( error )
) ;
}
}
Maintenant, nous allons quelque part!
Étant donné que nous avons défini les données en tant que variable de membre sur notre composant TypeScript, il est directement accessible par le HTML associé. En fait, si vous utilisez Visual Studio Code, l'éditeur open-source de Microsoft, vous pouvez ajouter le plugin de service de langue angulaire pour obtenir l'achèvement du code JavaScript en HTML ! Incroyable. Et il est maintenu par l'équipe angulaire.
Remplacement des données factices avant:
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< mat-card class =" score-card " *ngFor =" let fixture of gameweek.fixtures " >
< span class =" home " > {{ fixture.homeTeamName }} </ span >
< span class =" score " > {{ fixture.result.goalsHomeTeam }} : {{ fixture.result.goalsAwayTeam }} </ span >
< span class =" away " > {{ fixture.awayTeamName }} </ span >
</ mat-card >
</ div >
</ div >
Remarque le gameweek?.fixtures
?
Le symbole agit comme une courte main pour if (gameweek != null) { return gameweek.fixtures } else { return null }
, et il est incroyablement utile lors de l'accès à des variables qui ne seront remplies que des appels de repos asynchrones.
Et le tour est joué!
Cette partie suivante n'est pas strictement nécessaire, mais elle illustre une manière angulaire importante de faire les choses, et aidera certainement à garder notre code modulaire et à contenir si nous décidons de le faire avancer (jetez un œil à NativeScript comme un moyen de créer un Application mobile native avec angulaire et dactylographié).
Nous allons séduire la carte de score du luminaire dans son propre composant. Commencez par l'aide de la CLI: ng generate component score-card
(ou ng gc score-card
). Cela créera un fichier .ts
, .html
et .css
dans src/app/score-card
.
Ouvrez score-card.component.ts
pour être accueilli par un décorateur familier:
...
@ Component ( {
selector : 'app-score-card' , // Note this!
templateUrl : './score-card.component.html' ,
styleUrls : [ './score-card.component.css' ]
} )
...
Remarquez le champ selector
- il nous explique comment accéder au composant (dans ce cas à l'aide de balises <app-score-card></app-score-card>
>).
Refactor Notre code en déplaçant la viande à partir app.component.html
sur score-card.component.html
:
<!-- app.component.html -->
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< app-score-card *ngFor =" let fixture of gameweek?.fixtures " [fixture] =" fixture " > </ app-score-card >
</ div >
</ div >
<!-- score-card.component.html -->
< mat-card class =" score-card " >
< span class =" home " > {{ fixture.homeTeamName }} </ span >
< span class =" score " >
{{ fixture.result.goalsHomeTeam }} : {{ fixture.result.goalsAwayTeam }}
</ span >
< span class =" away " > {{ fixture.awayTeamName }} </ span >
</ mat-card >
Remarquez le bit [fixture]="fixture"
à l'intérieur des balises <app-score-card>
. C'est ainsi que nous transmettons des informations entre les composants.
Dans la syntaxe angulaire, [...]
indique les entrées, (…)
désignent les sorties et [(…)]
indique une liaison bidirectionnelle. [(…)]
Est également appelé "Syntaxe des boîtes de banane", et vous le rencontrerez souvent sous la forme de [(ngModel)]="someVariable"
. Cela implique une liaison bidirectionnelle entre la valeur d'une variable et la valeur d'un objet DOM. C'est un élément clé de l'utilisation d'angulaires.
Par exemple, nous pourrions mapper la valeur d'une balise input
directement sur une variable qui s'affiche à l'écran, et le DOM sera automatiquement mis à jour chaque fois que la valeur de l'élément input
change:
< p >
What is your name?
< input type =" text " [(ngModel)] =" name " />
</ p >
< p >
Your name: {{ name }}
</ p >
Vous pouvez consulter un exemple Plunker ici.
Retour au football: Afin de recevoir la valeur d'entrée dans le composant, nous devons également mettre à jour score-card.component.ts
comme suit:
import { Component , Input } from '@angular/core' ;
import { Fixture } from '../models/fixture' ;
@ Component ( {
selector : 'app-score-card' ,
templateUrl : './score-card.component.html' ,
styleUrls : [ './score-card.component.css' ]
} )
export class ScoreCardComponent {
@ Input ( ) fixture : Fixture ; // Note the decorator
constructor ( ) { }
}
Il existe deux façons évidentes de transmettre des données entre les composants: utiliser @Input
/ @Output
et utiliser des services.
@Input()
La première méthode est utile pour passer des données entre les parents et les enfants, mais peut être fastidieuse si les données doivent voyager à travers des couches imbriquées. Les données peuvent être transmises à un composant enfant à l'aide du décorateur @Input
. Cette variable d'entrée sera transmise par référence s'il s'agit d'un objet ou transmis par valeur s'il s'agit d'une primitive.
// someComponent.ts
// ...
import { Input } from '@angular/core'
// ...
export class SomeComponent {
// @Input() variables get set after the `constructor(...)`
// method, but before `ngOnInit()` fires
@ Input ( ) aNumber : number ; // This gets set via parent HTML
// ...
}
<!-- someComponentParent.html -->
< h1 >
I am a parent where SomeComponent gets rendered
</ h1 >
<!-- Pass a value to the aNumber variable -->
< some-component [aNumber] =" 48 " > </ some-component >
@Output()
Les données peuvent également être émises par l'enfant vers un composant parent à l'aide du décorateur @Output()
. Ce n'est pas une liaison directionnelle, mais plutôt un émetteur d'événements qui tire sur les temps prédéfinis. Un cas d'utilisation typique serait de notifier le parent lorsqu'une valeur est modifiée chez l'enfant.
// someComponent.ts
// ...
import { Input , Output , EventEmitter } from '@angular/core'
// ...
export class SomeComponent {
@ Input ( ) aNumber : number ;
// Emits an event (of type `number`) to the parent
@ Output ( ) numberChanged : EventEmitter < number > = new EventEmitter < number > ( ) ;
// ...
// Event emitters need to be triggered manually
// Any object can be emitted
emitValueChanged ( ) : void {
this . numberChanged . emit ( this . aNumber ) ;
}
}
<!-- someComponentParent.html -->
< h1 >
I am a parent where SomeComponent gets rendered
</ h1 >
<!-- Pass a value to the aNumber variable -->
< some-component [aNumber] =" 48 " (valueChanged) =" aNumberChanged($event) " > </ some-component >
// someComponentParent.ts
export class SomeParentComponent {
// ...
aNumberChanged ( updatedNumber : number ) : void {
console . log ( `aNumber changed to ${ updatedNumber } ` ) ;
}
// ...
}
Utilisation de services
Il existe de nombreuses façons d'utiliser des services pour transmettre des données entre les composants. Les détails dépassent le champ d'application de ce tutoriel, mais il convient de noter l'existence de telles techniques, car elle sera probablement nécessaire dans toute application relativement complexe.
Le modèle général est de définir un flux Observable
dans le service, auquel les composants peuvent pousser des messages ou s'abonner pour être informé des nouveaux messages. Il s'agit effectivement d'un bus d'événements.
Dans le cas général, les messages doivent être saisis pour que les auditeurs puissent discerner lesquels sont applicables. En tant qu'exemple quelque peu frivole, le service pourrait émettre un PersonNameChangedEvent
sur lequel le PersonComponent
pourrait réagir tandis que le LandingPageComponent
pourrait choisir d'ignorer complètement cet événement.
Angular fournit un cadre monolithique et hautement avisé à l'intérieur pour créer des applications. Cela fournit l'avantage de la structure - la nature d'opinion du cadre aide l'utilisateur à concevoir des applications évolutives dès le départ, et une grande partie de la complexité est cachée au développeur. D'un autre côté, l'utilisation d'Angular (et TypeScript, d'ailleurs) introduit beaucoup de code de passe-partout qui pourrait vous ralentir si vous construisez une petite application, il vaut donc la peine d'envisager où l'application va avant de coriger à Angular.
La CLI angulaire a cependant parcouru un long chemin, et étant donné la quantité de levage de lourds qu'il fait, j'utiliserai probablement Angular pour presque tous les projets dans un avenir proche.