J'ai un élément personnalisé:
<div formControlName="surveyType">
<div *ngFor="let type of surveyTypes"
(click)="onSelectType(type)"
[class.selected]="type === selectedType">
<md-icon>{{ type.icon }}</md-icon>
<span>{{ type.description }}</span>
</div>
</div>
Lorsque j'essaye d'ajouter le formControlName, j'obtiens un message d'erreur:
ERREUR Erreur: aucun accesseur de valeur pour le contrôle de formulaire avec le nom: 'surveyType'
J'ai essayé d'ajouter ngDefaultControl
sans succès. Il semble que c'est parce qu'il n'y a pas d'entrée / sélection ... et je ne sais pas quoi faire.
Je voudrais lier mon clic à ce formControl afin que lorsque quelqu'un clique sur la carte entière, cela pousse mon «type» dans le formControl. C'est possible?
Réponses:
Vous ne pouvez utiliser
formControlName
que sur les directives qui implémententControlValueAccessor
.Implémenter l'interface
Donc, pour faire ce que vous voulez, vous devez créer un composant qui implémente
ControlValueAccessor
, ce qui signifie implémenter les trois fonctions suivantes :writeValue
(indique à Angular comment écrire la valeur du modèle dans la vue)registerOnChange
(enregistre une fonction de gestionnaire qui est appelée lorsque la vue change)registerOnTouched
(enregistre un gestionnaire à appeler lorsque le composant reçoit un événement tactile, utile pour savoir si le composant a été focalisé).Enregistrer un fournisseur
Ensuite, vous devez dire à Angular que cette directive est une
ControlValueAccessor
(interface ne la coupera pas car elle est supprimée du code lorsque TypeScript est compilé en JavaScript). Pour ce faire, vous enregistrez un fournisseur .Le fournisseur doit fournir
NG_VALUE_ACCESSOR
et utiliser une valeur existante . Vous aurez également besoin d'unforwardRef
ici. Notez que celaNG_VALUE_ACCESSOR
devrait être un fournisseur multiple .Par exemple, si votre directive personnalisée est nommée MyControlComponent, vous devez ajouter quelque chose le long des lignes suivantes à l'intérieur de l'objet passé au
@Component
décorateur:Usage
Votre composant est prêt à être utilisé. Avec les formulaires basés sur des modèles , la
ngModel
liaison fonctionnera désormais correctement.Avec les formulaires réactifs , vous pouvez désormais utiliser correctement
formControlName
et le contrôle de formulaire se comportera comme prévu.Ressources
la source
Je pense que vous devriez utiliser
formControlName="surveyType"
sur uninput
et non sur undiv
la source
<ng-content>
dans un composant wrapper et laisser le composant parent qui définitformControls
simplement mettre le <input> à l'intérieur du <wrapper>L'erreur signifie qu'Angular ne sait pas quoi faire lorsque vous mettez
formControl
undiv
. Pour résoudre ce problème, vous avez deux options.formControlName
sur un élément, qui est pris en charge par Angular hors de la boîte. Ce sont:input
,textarea
etselect
.ControlValueAccessor
interface. En faisant cela, vous dites à Angular "comment accéder à la valeur de votre contrôle" (d'où le nom). Ou en termes simples: que faire, lorsque vous mettez unformControlName
sur un élément, qui n'a naturellement pas de valeur associée.Désormais, la mise en œuvre de l'
ControlValueAccessor
interface peut être un peu intimidante au début. Surtout parce qu'il n'y a pas beaucoup de bonne documentation à ce sujet et que vous devez ajouter beaucoup de passe-partout à votre code. Alors laissez-moi essayer de décomposer cela en quelques étapes simples à suivre.Déplacez votre contrôle de formulaire dans son propre composant
Pour implémenter le
ControlValueAccessor
, vous devez créer un nouveau composant (ou directive). Déplacez-y le code lié à votre contrôle de formulaire. Comme ça, il sera également facilement réutilisable. Avoir un contrôle déjà à l'intérieur d'un composant peut être la raison en premier lieu, pourquoi vous devez implémenter l'ControlValueAccessor
interface, car sinon vous ne pourrez pas utiliser votre composant personnalisé avec des formulaires angulaires.Ajoutez le passe-partout à votre code
La mise en œuvre de l'
ControlValueAccessor
interface est assez verbeuse, voici le passe-partout qui l'accompagne:Alors, que font les différentes parties?
ControlValueAccessor
interfaceControlValueAccessor
interfaceonChange
etonTouch
avec sa propre implémentation pendant l'exécution, de sorte que vous puissiez ensuite appeler ces fonctions. Il est donc important de comprendre ce point: vous n'avez pas besoin d'implémenter onChange et onTouch vous-même (autre que l'implémentation vide initiale). La seule chose que vous faites avec (c) est de laisser Angular attacher ses propres fonctions à votre classe. Pourquoi? Vous pouvez donc appeler les méthodesonChange
etonTouch
fournies par Angular au moment opportun. Nous verrons comment cela fonctionne ci-dessous.writeValue
méthode fonctionne dans la section suivante, lorsque nous l'implémenterons. Je l'ai mis ici, donc toutes les propriétés requises surControlValueAccessor
sont implémentées et votre code se compile toujours.Implémenter writeValue
Ce que
writeValue
fait, c'est faire quelque chose à l'intérieur de votre composant personnalisé, lorsque le contrôle de formulaire est modifié à l'extérieur . Ainsi, par exemple, si vous avez nommé votre composant de contrôle de formulaire personnaliséapp-custom-input
et que vous l'utiliseriez dans le composant parent comme ceci:puis
writeValue
est déclenché chaque fois que le composant parent change d'une manière ou d'une autre la valeur demyFormControl
. Cela peut être par exemple lors de l'initialisation du formulaire (this.form = this.formBuilder.group({myFormControl: ""});
) ou lors d'une réinitialisation de formulairethis.form.reset();
.Ce que vous souhaiterez généralement faire si la valeur du contrôle de formulaire change à l'extérieur, c'est de l'écrire dans une variable locale qui représente la valeur du contrôle de formulaire. Par exemple, si votre
CustomInputComponent
tourne autour d'un contrôle de formulaire basé sur du texte, cela pourrait ressembler à ceci:et dans le html de
CustomInputComponent
:Vous pouvez également l'écrire directement dans l'élément d'entrée comme décrit dans la documentation Angular.
Vous avez maintenant géré ce qui se passe à l'intérieur de votre composant lorsque quelque chose change à l'extérieur. Regardons maintenant l'autre direction. Comment informez-vous le monde extérieur lorsque quelque chose change à l'intérieur de votre composant?
Appeler onChange
L'étape suivante consiste à informer le composant parent des modifications apportées à votre
CustomInputComponent
. C'est là que les fonctionsonChange
etonTouch
de (c) d'en haut entrent en jeu. En appelant ces fonctions, vous pouvez informer l'extérieur des changements à l'intérieur de votre composant. Afin de propager les modifications de la valeur vers l'extérieur, vous devez appeler onChange avec la nouvelle valeur comme argument . Par exemple, si l'utilisateur tape quelque chose dans leinput
champ de votre composant personnalisé, vous appelezonChange
avec la valeur mise à jour:Si vous vérifiez à nouveau l'implémentation (c) ci-dessus, vous verrez ce qui se passe: Angular lié sa propre implémentation à la
onChange
propriété de classe. Cette implémentation attend un argument, qui est la valeur de contrôle mise à jour. Ce que vous faites maintenant, c'est que vous appelez cette méthode et que vous informez Angular du changement. Angular va maintenant continuer et changer la valeur du formulaire à l'extérieur. C'est la partie clé de tout cela. Vous avez indiqué à Angular quand il doit mettre à jour le contrôle de formulaire et avec quelle valeur en appelantonChange
. Vous lui avez donné le moyen "d'accéder à la valeur de contrôle".Au fait: le nom
onChange
est choisi par moi. Vous pouvez choisir n'importe quoi ici, par exemplepropagateChange
ou similaire. Quelle que soit la façon dont vous le nommez, ce sera la même fonction qui prend un argument, qui est fourni par Angular et qui est lié à votre classe par laregisterOnChange
méthode pendant l'exécution.Appeler surTouch
Puisque les contrôles de formulaire peuvent être «touchés», vous devez également donner à Angular les moyens de comprendre quand votre contrôle de formulaire personnalisé est touché. Vous pouvez le faire, vous l'avez deviné, en appelant la
onTouch
fonction. Donc, pour notre exemple ici, si vous voulez rester conforme à la façon dont Angular le fait pour les contrôles de formulaire prêts à l'emploi, vous devez appeleronTouch
lorsque le champ de saisie est flou:Encore
onTouch
une fois, c'est un nom que j'ai choisi, mais sa fonction réelle est fournie par Angular et ne prend aucun argument. Ce qui est logique, puisque vous faites juste savoir à Angular, que le contrôle de formulaire a été touché.Mettre tous ensemble
Alors, à quoi cela ressemble-t-il quand tout est réuni? Ça devrait ressembler à ça:
Plus d'exemples
Formulaires imbriqués
Notez que les accesseurs de valeur de contrôle ne sont PAS le bon outil pour les groupes de formulaires imbriqués. Pour les groupes de formulaires imbriqués, vous pouvez simplement utiliser un
@Input() subform
fichier. Les accesseurs de valeur de contrôle sont destinés à enveloppercontrols
, nongroups
! Voir cet exemple comment utiliser une entrée pour un formulaire imbriqué: https://stackblitz.com/edit/angular-nested-forms-input-2Sources
la source
Pour moi, cela était dû à l'attribut "multiple" sur le contrôle d'entrée sélectionné car Angular a un ValueAccessor différent pour ce type de contrôle.
Et à l'intérieur du modèle, utilisez comme ça
Plus de détails réf Docs officiels
la source