Du traducteurD'un traducteur : il y a deux ans, j'ai commencé mon premier projet sur Angular (2+), ayant une grande et réussie expérience AngularJS. La transition a nécessité un formatage sensible de la pensée, car trop sur A1 et A2 + se fait «un peu différemment». La douleur de la transition a considérablement réduit le blog de
thinktram pour
moi . Il y a un an, j'ai reçu la permission de traduire cet article "sur élémentaire et facilement compréhensible pour tout le monde". Mais ce sont de telles mains (leurs articles sont un tas de livres inachevés). Étonnamment, l'article se traduit bien sur Google translate. Mais certaines des nuances de cette traduction ont été perdues, sans parler du style de l'auteur. Le style de l'auteur n'a pas été entièrement conservé dans ma version. Mais, j'espère, j'ai réussi à transmettre l'humeur et les pensées de l'article.
Je comprends que Angular n'est pas le sujet le plus populaire sur Habré, mais j'espère que la traduction aidera quelqu'un, tout comme l'article original m'a aidé une fois.
C'est ce qui a causé l'effet wow dans le bon vieux AngularJS, c'est donc une "liaison bidirectionnelle". Cette magie est instantanément tombée amoureuse d'AngularJS et a brisé toutes les idées sur la programmation de pages ennuyeuses et (oh, l'horreur!) Les formulaires Web. Les modifications apportées aux données sont instantanément affichées à l'écran et vice versa. Ceux qui ont précédemment développé des applications jQuery ont perçu la liaison comme tombant dans un conte de fées. Et les monstres barbus, voyant des gros clients avant jQuery, ont commencé à compter frénétiquement les mois-hommes stupidement perdus.
De plus, la magie de la reliure bidirectionnelle était disponible non seulement pour les notations spéciales et les composants sélectionnés. Nous pourrions facilement l'utiliser dans nos propres directives et composants (simplement en définissant le paramètre de configuration).
Dans Angular2 +, les créateurs ont abandonné la liaison de données bidirectionnelle intégrée (sauf via ngModel). Mais cela ne signifie pas que nous ne pouvons pas utiliser la liaison bidirectionnelle dans nos propres directives ... C'est juste que le cadeau est terminé et maintenant nous devons faire quelque chose par nous-mêmes. Et, de préférence, avec une compréhension de la façon dont cela fonctionne dans Angular.
Table des matières
Reliure bidirectionnelle en bref
Dans A2 +, une seule directive implémente la liaison de données bidirectionnelle:
ngModel . Et à première vue, c'est la même magie que dans AngularJS (uniquement dans une notation différente). Mais qu'est-ce qui se cache sous le capot?
Étonnamment, sous le capot, tout est relativement simple et logique: la liaison bidirectionnelle est réduite à la liaison de propriété et à la liaison d'événement. Deux consolidations unilatérales, au lieu d'une bilatérale? Ok, disons deux.
Et immédiatement un exemple:
<input [(ngModel)]="username"> <p>Hello {{username}}!</p>
Oui, oui, c'est une belle et étonnante démo Angular2 de 2009. Sans blague, magnifique. Lors de la modification du champ, la valeur du
nom d'utilisateur tombe dans le modèle et est immédiatement reflétée dans le message de bienvenue sur le formulaire.
Mais comment ça marche? Rappelons que la liaison bidirectionnelle dans Angular2 est une liaison de propriété et une liaison d'événement. Et oui, ils peuvent être disponibles simultanément dans une seule directive. De plus, même sans
ngModel , nous pourrions facilement implémenter une liaison de données bidirectionnelle. Par exemple, comme ceci:
<input [value]="username" (input)="username = $event.target.value"> <p>Hello {{username}}!</p>
La sortie
{{username}} est claire, mais qu'est-ce qui y est écrit en
entrée ? Comprenons:
- [valeur] = «nom d'utilisateur» - notation entre crochets, associe l'expression du nom d'utilisateur à la propriété value
- (entrée) = "expression" - une notation entre parenthèses, l'expression est attachée à l'événement d' entrée (oui, il y a un tel événement). Dans notre cas:
- username = $ event.target.value - cette expression sera exécutée en réponse à l'événement d'entrée
- $ event est une variable synthétique dans les événements angulaires qui porte une charge utile: dans ce cas, il contient des informations sur ce qui s'est passé et ses environs
Est-ce que ça devient plus clair? Nous le réparons.
Nous lions la propriété
username du modèle angulaire à la propriété
value de l'élément d'entrée du navigateur (liaison unidirectionnelle du modèle à la vue).
Nous lions également une expression à l'événement d'
entrée de notre élément. Qui affecte la valeur de
$ event.target.value à la propriété de
nom d'utilisateur du modèle.
Qu'est-ce que
$ event.target.value ? Comme déjà mentionné,
$ event regorge de diverses informations utiles sur l'événement. Dans ce cas, il s'agit d'un
InputEventObject dans lequel la propriété
target fait référence à l'élément DOM qui a
déclenché l' événement (c'est-à-dire notre élément d'entrée).
Donc, tout ce que nous faisons essentiellement, c'est lire le contenu (
valeur ) de l'élément d'entrée (
$ event.target ) lorsque l'utilisateur entre une valeur. Et lorsque nous attribuons cette valeur de nom d'utilisateur, les données de vue seront envoyées au modèle.
C’est tout. Il s'agit d'une "liaison bidirectionnelle en bref" . La beauté?
Mais quand
ngModel entre-t-il en jeu? Le scénario de travail avec des éléments d'entrée est très courant et très demandé. Et pour une raison quelconque, je veux avoir une directive qui masque l'implémentation et économise des frappes supplémentaires.
Comprendre ngModel
Si vous regardez la source, vous pouvez vous assurer que
ngModel a également une liaison à la propriété et à l'événement. Voici à quoi ressemble notre exemple ngModel, mais sans utiliser de syntaxe abrégée:
<input [ngModel]="username" (ngModelChange)="username = $event"> <p>Hello {{username}}!</p>
Presque tout est pareil. La liaison de la
propriété [ngModel] se charge de mettre à jour la valeur de l'élément d'entrée. Une liaison d'événement
(ngModelChange) informe le monde que des changements se produisent dans le DOM.
Et vous avez remarqué que l'expression du gestionnaire utilise uniquement
$ event , pas
$ event.target.value . Quelque chose ne va pas ici? Pas du tout. Comme indiqué ci-dessus,
$ event est une variable synthétique qui transporte une
charge utile . La décision de ce qui est considéré comme utile est prise par Angular. En d'autres termes,
ngModelChange se charge d'extraire
target.value de l'
événement $ interne et nous donne simplement ce que nous voulons, sans empaquetage ni tambourin. Pour être techniquement précis, ce sont ceux de
DefaultValueAccessor : c'est lui qui extrait les données et les transfère à l'objet DOM de base, bien que ... vous ne pouvez tout simplement pas y penser).
Dernier point mais non des moindres, puisque l'écriture deux fois de
nom d'utilisateur et de
ngModel est toujours redondante, Angular permet l'utilisation de la syntaxe abrégée
[()] , également appelée «banane dans une boîte». Ce qui est similaire à l'exemple précédent, et nous renvoie à l'exemple du début de la section, mais avec une compréhension de l'implémentation de
ngModel . Fournir la même liaison bidirectionnelle.
<input [(ngModel)]="username"> <p>Hello {{username}}!</p>
Créez vos propres liaisons de données bidirectionnelles
Nous en savons maintenant assez pour créer nos propres liaisons de données bidirectionnelles. Il vous suffit de suivre les mêmes règles que
ngModel , à savoir:
- Entrez une liaison de propriété (par exemple: [foo] )
- Lier à un événement portant le même nom et le même suffixe Change (par exemple: (fooChange) )
- Assurez-vous que la liaison d'événement prend en charge la récupération de la propriété (si nécessaire)
Notez que la création d'une liaison de données bidirectionnelle nécessite beaucoup plus de travail que AngularJS? Cela pourrait être très frustrant pour nous ... Si nous essayions d'utiliser notre propre liaison bidirectionnelle dans la mesure du possible. Dans la vraie vie, vous devez toujours vous demander si nous avons besoin d'une liaison bidirectionnelle et, si nécessaire, s'il est plus facile de profiter de ngModel. Ce dernier, par exemple, a lieu lors de la création de
contrôles de formulaire personnalisés .
Mais disons que nous créons un composant de compteur personnalisé (et que nous ne voulons pas utiliser un contrôle de formulaire personnalisé).
@Component({ selector: 'custom-counter', template: ` <button (click)="decrement()">-</button> <span>{{counter}}</span> <button (click)="increment()">+</button> ` }) export class CustomCounterComponent { counterValue = 0; get counter() { return this.counterValue; } set counter(value) { this.counterValue = value; } decrement() { this.counter--; } increment() { this.counter++; } }
Nous avons la propriété du composant
compteur pour afficher la valeur actuelle du compteur. Pour lui fournir une liaison bidirectionnelle, la première chose à faire est de le transformer en paramètre d'
entrée . Pour cela, le décorateur
@Input () est très utile:
@Component() export class CustomCounterComponent { counterValue = 0; @Input() get counter() { return this.counterValue; } ... }
Cela vous permet déjà de lier la propriété du composant au consommateur comme suit:
<custom-counter [counter]="someValue"></custom-counter>
Maintenant, nous devons définir l'
événement @Output () avec le même nom (
compteur ) et le suffixe
Change (il s'avère que counterChange). Nous voulons soulever cet événement à chaque fois que le
compteur change. Pourquoi ajouter la propriété
@Output () . Et nous terminerons, dans quelques getters, le compteur de compteur, dans lequel nous intercepterons le changement de valeur et lancerons un événement avec la valeur de compteur actuelle:
@Component() export class CustomCounterComponent { ... @Output() counterChange = new EventEmitter(); set counter(val) { this.counterValue = val; this.counterChange.emit(this.counterValue); } ... }
Ça y est! Maintenant, nous pouvons lier l'expression à cette propriété en utilisant la syntaxe de liaison de données bidirectionnelle:
<custom-counter [(counter)]="someValue"></custom-counter> <p>counterValue = {{someValue}}</p>
Découvrez la
démo et essayez-la!
Encore une fois, gardez à l'esprit qu'un composant tel qu'un compteur personnalisé est mieux implémenté avec un contrôle de formulaire personnalisé et profitez de
ngModel pour implémenter la liaison de données bidirectionnelle, comme décrit dans
cet article .
Conclusion
Angular n'est plus fourni avec une liaison de données bidirectionnelle intégrée. Au lieu de cela, il y a des API dans la boîte qui vous permettent d'implémenter la liaison complète en tant que propriétés et événements de liaison.
ngModel est une directive de liaison bidirectionnelle intégrée dans FormsModule (n'oubliez pas de l'ajouter à la section des
importations de la déclaration
@NgModule : environ par). La liaison via ngModel doit être préférée lors de la création de composants qui servent de contrôles de formulaire personnalisés. Sinon, tout dépend de votre imagination.
PS du traducteur: l'implémentation de liaison dans A2 + est devenue plus moderne. Désormais, des setters presque «gratuits» sont utilisés pour surveiller les changements par le «feng shui» (bien qu'il soit clair que les mécanismes de vérification des erreurs restent, au moins pour les composants utilisateurs de haut niveau). Cela a permis d'abandonner 100 500 observateurs (procédures de suivi des évolutions de «leurs» données). Qui dans A1 aimait créer une charge malveillante sur le navigateur et nécessitait des mains inhabituellement directes lors de la planification de pages interactives riches.
Avec des composants correctement conçus, A2 prêt à l'emploi est devenu beaucoup plus réactif. Laissez au détriment du travail des programmeurs. Vous pouvez maintenant placer une légion de composants sur la page et ne vous souciez pas des ressources du processeur.
Le revers de la médaille était le coût initial du "processus d'entrée" dans A2 +, qui a affecté la popularité du cadre. Mais A1 avait également un coût d'entrée élevé, seulement il a été relégué dans la ligue majeure. En raison d'un manque de compréhension sur la façon d'organiser de grandes applications, de nombreux prototypes "ont décollé" sur A1, puis "se sont effondrés" et correspondaient à React et Vue.
J'espère qu'avec cet article, j'aiderai à abaisser légèrement le seuil d'entrée initiale à A2 +, qui continue d'être en demande (que je connais de première main).