Pourquoi passer aux signaux d’entrée : au revoir, @Input() en angulaire

Vous connaissez le décorateur d’entrée dans Angular. Saviez-vous qu’Angular 17 nous a apporté des entrées de signaux ? Les signaux permettent aux types primitifs de réagir aux changements, et nous pouvons désormais le faire via l’entrée ! Cela signifiera un code plus propre et plus facile à maintenir !
Dans Angular, au cours des dernières années, une façon de communiquer entre les composants a consisté à utiliser le @Input()
décorateur. Cela nous aide à transmettre des informations entre les composants. Cependant, lorsque nous utilisons le @Input()
décorateur, il est le plus souvent associé à ngOnChanges
et le SimpleChanges
tapez pour réagir aux changements dans l’application.
Le introduction des signaux marque un changement significatif dans le fonctionnement de la réactivité dans Angular. Les signaux permettent aux types primitifs de réagir aux changements, facilitant ainsi les interactions et, surtout, réduisant la quantité de code écrit.
Aujourd’hui, j’ai une excellente nouvelle pour vous. Dans Angular 17.1, nous avons signal()
contributions! Cela permet d’exploiter toute la puissance des signaux dans nos entrées. Nous couvrirons la plupart des options et comparerons l’original @Input()
avec la nouvelle entrée de signaux, apprenez par la pratique comment les entrées de signaux peuvent réduire le passe-partout, les crochets du cycle de vie et bien plus encore.
Comme toujours, la meilleure façon d’apprendre est de passer par un scénario du monde réel ! Faisons-le!
Scénario
Nous voulons avoir un composant qui affiche une grille définie sur certains paramètres de données. Son titre devrait refléter cette configuration. Voici où nous nous dirigeons :
Pour simplifier la liaison des propriétés de la grille kendo, nous pouvons créer un nouveau GridConfig
objet. Le composant fournira deux propriétés d’entrée, data
et config
et couvrent en même temps les cas suivants.
- Transmettez un objet de configuration avec trois propriétés :
title
,enabledPagination
,itemsPerPage
. - Le titre est facultatif.
- La configuration n’est pas requise.
- Si la configuration n’est pas définie, définissez une valeur par défaut.
- Les données doivent être obligatoires.
- Afficher un titre lorsque la configuration est vide ou manquante.
Il s’agit d’un processus étape par étape, mais l’objectif principal est de voir les différences entre @Input
et les nouvelles entrées de signal. Alors, codons !
Configurer le projet
Tout d’abord, configurez votre application Angular avec la commande ng new using-signal-inputs
.
ng new using-signal-inputs
cd using-signal-inputs
npm install
Installez ensuite l’interface utilisateur de Kendo pour Grille angulaireen utilisant une commande schématique pour enregistrer :
ng add @progress/kendo-angular-grid
UPDATE src/app/app.module.ts (515 bytes)
UPDATE package.json (1708 bytes)
UPDATE angular.json (3165 bytes)
√ Packages installed successfully.
UPDATE src/main.ts (259 bytes)
UPDATE tsconfig.app.json (294 bytes)
UPDATE tsconfig.spec.json (300 bytes)
Nous sommes tous configurés, alors passons à notre composant wrapper.
Le composant Wrapper
Tout d’abord, nous générons le composant à l’aide de la commande Angular CLI ng generate component /components/invoices
:
ng g c /components/invoices
CREATE src/app/components/invoices/invoices.component.html (24 bytes)
CREATE src/app/components/invoices/invoices.component.spec.ts (633 bytes)
CREATE src/app/components/invoices/invoices.component.ts (255 bytes)
CREATE src/app/components/invoices/invoices.component.scss (0 bytes)
Ouvrez le invoice.component.ts
fichier et importer le GridModule
dans la section importations :
import {Component } from '@angular/core';
import {GridModule} from "@progress/kendo-angular-grid";
@Component({
selector: 'app-invoices',
standalone: true,
imports: [
GridModule
],
templateUrl: './invoices.component.html',
styleUrl: './invoices.component.scss'
})
export class InvoicesComponent {
}
Définir GridConfig et defaultConfig
Nous devons remplir les deux exigences suivantes :
- Transmettez un objet de configuration avec trois propriétés :
title
,enablePagination
etitemsPerPage
. - Rendre le titre facultatif.
Pour faire simple, en plus du @Component des factures, déclarez le type du GridConfig
tapez en fonction des exigences.
Le ?
définit la propriété title comme facultative :
export type GridConfig = {
title?: string;
enablePagination: boolean;
itemsPerPage: number;
}
Définissez la configuration par défaut à utiliser lorsque l’utilisateur ne fournit pas la configuration :
const defaultConfig : GridConfig = {
title: 'Default Config',
enablePagination: true,
itemsPerPage: 3
}
Le invoices.component.ts
doit ressembler à :
import {Component } from '@angular/core';
import {GridModule} from "@progress/kendo-angular-grid";
export type GridConfig = {
title?: string;
enablePagination: boolean;
itemsPerPage: number;
}
const defaultConfig : GridConfig = {
title: 'Default Config',
enablePagination: true,
itemsPerPage: 3
}
@Component({
selector: 'app-invoices',
standalone: true,
imports: [
GridModule
],
templateUrl: './invoices.component.html',
styleUrl: './invoices.component.scss'
})
export class InvoicesComponent {
}
Utilisation du décorateur @Input(), ngOnChanges et SimpleChanges
Nous devons fournir deux entrées pour permettre la transmission des informations du app.component
. Déclarez deux @Input()
propriétés, config
et data
. Se souvenir du data
est requis. Depuis Angular 16, nous avons le paramètre { required: true }
pour forcer la transmission de l’entrée.
@Input() config!: GridConfig;
@Input({ required: true}) data: Array<unknown> = []
Parfait. Nous avons maintenant deux autres exigences :
- Si la configuration est vide, définissez un defaultConfig.
- Afficher un titre lorsque la configuration est vide ou manquante.
Comment pouvons-nous savoir quand les entrées ont été mises à jour ou modifiées et mettre à jour le titre de la configuration ? Eh bien, pour cela, nous utilisons le ngOnChanges
crochet de cycle de vie en implémentant le OnChanges
interface.
Nous utilisons le ngOnChanges
crochet pour détecter quand les entrées ont changé et recevoir le SimpleChanges
object en tant que paramètre pour obtenir chaque changement de clé.
export class InvoicesComponent implements OnChanges {
@Input() config!: GridConfig;
@Input({ required: true}) data: Array<unknown> = []
ngOnChanges(changes: SimpleChanges): void {
}
}
Pour relever notre défi, nous devons lire les changements dans le config
propriété et définissez la valeur par défaut sur non défini. Pour obtenir le config
des modifications, pour notre application, nous pouvons y accéder par le nom de clé de config
. S’il n’existe pas, nous définissons la valeur par défaut et quittons.
if(!changes['config']) {
this.config = defaultConfig;
return;
}
Mais si la configuration existe, nous pouvons alors obtenir le titre. S’il est vide, nous définissons un message personnalisé tel que « Config sans titre ».
const { title } = changes['config'].currentValue;
if(!title) {
this.config.title = 'Config without Title'
}
Le ngOnChanges
devrait maintenant ressembler à :
ngOnChanges(changes: SimpleChanges): void {
if(!changes['config']) {
this.config = defaultConfig;
return;
}
const { title } = changes['config'].currentValue;
if(!title) {
this.config.title = 'Config without Title'
}
}
En savoir plus sur Changements simples et Sur les modifications.
Le code final dans le invoices.component.ts
devrait maintenant ressembler à :
import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
import {GridModule} from "@progress/kendo-angular-grid";
export type GridConfig = {
title?: string;
enablePagination: boolean;
itemsPerPage: number;
}
const defaultConfig : GridConfig = {
title: 'Default Config',
enablePagination: true,
itemsPerPage: 3
}
@Component({
selector: 'app-invoices',
standalone: true,
imports: [
GridModule
],
templateUrl: './invoices.component.html',
styleUrl: './invoices.component.scss'
})
export class InvoicesComponent implements OnChanges {
@Input() config!: GridConfig;
@Input({ required: true}) data: Array<unknown> = []
ngOnChanges(changes: SimpleChanges): void {
if(!changes['config']) {
this.config = defaultConfig;
return;
}
const { title } = changes['config'].currentValue;
if(!title) {
this.config.title = 'Config without Title'
}
}
}
Nous devons utiliser les valeurs de configuration, y compris le titre, et lier les propriétés kendo-grid. Utilisez un <h2>
balise pour le titre et lie le kendoGridBinding
, pageable
et pageSize
propriétés avec le config.
Le modèle devrait ressembler à ceci :
<h2>{{config.title}}</h2>
<kendo-grid
[kendoGridBinding]="data"
[pageable]="config.enablePagination" [pageSize]="config.itemsPerPage">
</kendo-grid>
Le composant facture est prêt. Passons aux étapes suivantes.
Les données simulées
Avant d’utiliser le composant de facture, créez des données fictives credit-cards.ts
dans app/mock
:
exporter const CARTES DE CRÉDIT = [{
"card_number": "3561737837553025",
"card_holder_name": "Jakob Parry",
"expiration_date": "3/12/2027",
"cvv": 385,
"card_limit": "Ruble"
}, {
"card_number": "6706057341869513",
"card_holder_name": "Chloette Weiser",
"expiration_date": "12/24/2025",
"cvv": 696,
"card_limit": "Dollar"
}
Get the full credit-card.ts file.
Passing Inputs
We are ready to use the InvoiceComponent
in the app. Before we proceed, there are some steps we must take.
- Import the
CREDIT_CARDS
mock data andGridConfig
. - Register the
InvoiceComponent
in the imports array (we are using standalone components 🥰 ).
import { GridConfig, InvoicesComponent} from "./components/invoices/invoices.component";
import {CREDIT_CARDS} from "./mock/credit-cards";
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, InvoicesComponent],URL du modèle: './app.component.html',styleUrl: './app.component.scss'
})
exporter classe Composant d'application {
Ensuite, créez deux configurations différentes : une avec un titre et une autre sans.
config: GridConfig = {
title: 'Purchase Invoices',
itemsPerPage: 7,
enablePagination: true,
}
configWithoutTitle: GridConfig = {
itemsPerPage: 5,
enablePagination: true
}
Le final app.component.ts
devrait maintenant ressembler à :
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { GridConfig, InvoicesComponent} from "./components/invoices/invoices.component";
import {CREDIT_CARDS} from "./mock/credit-cards";
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, InvoicesComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
title = 'using-signal-inputs';
protected readonly CREDIT_CARDS = CREDIT_CARDS;
config: GridConfig = {
title: 'Purchase Invoices',
itemsPerPage: 7,
enablePagination: true,
}
configWithoutTitle: GridConfig = {
itemsPerPage: 5,
enablePagination: true
}
}
Nous sommes prêts à utiliser le <app-invoices>
composant, le CREDIT_CARDS
des données simulées et nos configurations. Ajoute un <app-invoices>
composant pour chaque configuration et cas :
<app-invoices [data]="CREDIT_CARDS"></app-invoices>
<app-invoices [config]="config" [data]="CREDIT_CARDS"></app-invoices>
<app-invoices [config]="configWithoutTitle" [data]="CREDIT_CARDS"></app-invoices>
Enregistrez les modifications et exécutez ng serve -o
pour lancer l’application ! L’application affichera le <app-invoices>
composant, en utilisant @Input()
pour transmettre la configuration entre les composants.
Cela fonctionne comme prévu ! Ouais!! Mais… s’il vous plaît, ne vous fâchez pas contre moi… nous pouvons le faire avec moins de code en utilisant des entrées de signal !
Entrées de signaux
Les entrées de signal sont une nouvelle façon réactive de permettre la communication en utilisant des entrées basées sur des signaux des composants parent vers enfants. L’entrée est livrée avec toutes les fonctionnalités @Input() comme transform
, alias
et .required
.
En utilisant des entrées de signal, nous avons tapez en toute sécurité SimpleChanges au lieu d’une table de hachage.
Les entrées de signal nécessitent une valeur initiale, et lorsque nous les combinons avec des transformations, elles déclenchent automatiquement une vérification pour correspondre aux valeurs d’entrée acceptées et créent une nouvelle valeur à partir des modifications d’entrée en utilisant computed
.
Refactorisons donc notre code pour utiliser les entrées de signal.
En savoir plus sur entrées de signaux.
Refactoriser l’entrée de signal
Ouvrez le invoice.component.ts
fichier, supprimez le ngOnChanges
méthode (avec sa fonctionnalité interne) et supprimez le implements OnChanges
interface sur le composant également. Ensuite, mettez à jour le data
propriété de la @Input
décorateur avec le input
fonction. Le input
La fonction prend également en charge le required
option, alors changez-la en input.required
et définissez le type comme un tableau d’inconnus.
data = input.required<Array<unknown>>();
Passons maintenant à la partie clé : le config
. Je vais refactoriser cela étape par étape et apporter quelques modifications pour améliorer le code. Faisons-le:
- Parce que
input
est un signal, nous pouvons définir une valeur initiale. Par conséquent, lors de l’initialisation, définissez ledefaultConfig
.
config = input(defaultConfig)
- Le
config
est le nom de notre consommateur public, mais en interne, nous savons qu’il s’agit de Kendo UI Grid, alors pourquoi ne pas le renommer enkendoConfig
pour le composant facture et utilisez unalias
pour usage externe commeconfig
:
kendoConfig = input(
defaultConfig,
{
alias: 'config'}
)
- Enfin, nous utilisons le
transform
fonction pour vérifier si letitle
est vide. Si tel est le cas, nous définissons un message personnalisé ou utilisons le message par défaut :
transform: (config: GridConfig) => {
config.title = config?.title || 'Config but without title'
return config
}
Sans avoir besoin de ngOnChanges
ou SimpleChanges
le code final de notre invoices.component.ts
pourrait ressembler à ceci :
export class InvoicesComponent {
kendoConfig = input( defaultConfig,
{
alias: 'config',
transform: (config: GridConfig) => {
config.title = config?.title || 'Config but without title'
return config
}
});
data = input.required<Array<unknown>>();
}
Enfin, dans le modèle, car kendoConfig
est un signal, nous devons mettre à jour le nouveau nom et l’appeler avec ()
.
<h2>{{kendoConfig().title}}</h2>
<kendo-grid
[kendoGridBinding]="data()"
[pageable]="kendoConfig().enablePagination" [pageSize]="kendoConfig().itemsPerPage">
</kendo-grid>
Enregistrez les modifications et tout continue de fonctionner avec moins de code et est plus facile à lire !
Conclusion
Nous avons appris que les entrées de signaux sont incroyables pour garder notre code réactif. Ils fonctionnent de la même manière que les classiques @Input
mais ils sont désormais basés sur des signaux et offrent toutes les mêmes fonctionnalités : required
, alias
et transform
. Cette approche évite l’utilisation du ngOnChanges
crochet de cycle de vie lorsque nous écrivons des composants réactifs à l’aide de signaux !
Je recommande fortement d’adopter Signal
. Vous pouvez en savoir plus sur signauxou vous pouvez lire le documentation officielle.
Bon codage !
Source link