Title: Passing formControlName into Angular component
Tags: angular;angular-reactive-forms;angular-components;controlvalueaccessor
Question: I have a reactive form. The setup is similar to this:
myForm: FormGroup;
this.myForm= new FormGroup({
name: new FormControl("", [Validators.required, Validators.maxLength(15), Validators.pattern('...')]),
...
});
I use this on my form like this:
<input
type="text"
formControlName="name"
/&gt;
<div *ngIf="name.errors?.required"&gt;
Name is required
</div&gt;
<div *ngIf="name.errors?.maxlength"&gt;
Name must be {{ name.errors.maxlength.requiredLength }} characters
</div&gt;
<div *ngIf="name.errors?.pattern"&gt;
Name has invalid characters.
</div&gt;
This is just a cut down version of my form. I have multiple input and I've had to create the error div's for each input.
So to fix this I've tried to create a component. The component is very similar to the code above:
<input
type="text"
[formControlName]="formControlName"
/&gt;
<div *ngIf="name.errors?.required"&gt;
Name is required
</div&gt;
etc...
ts file:
@Component({
selector: 'app-text',
templateUrl: './text.component.html'
export class TextComponent  {
@Input() formControlName: FormControl;
So on my form I'd like to use this component as follows:
<app-text [formControlName]="name"&gt;</app-text&gt;
But I can't get this to work with the formControlName property.
Is this possible?
Thanks
I'm nearly there.
I've create this StackBlitz so show my progress:
Just struggling with the errors now and how to access the formControl to check for those errors
Comment: why do you want that formControl in the TextComponent?

Comment: Can you provide stackblitz?

Comment: So I can use the validators to show the errors in the component thus not duplicating them everywhere

Comment: OK, I nearly have this working but I'm still struggling the errors part. How do I access the formControl to check for errors? https://stackblitz.com/edit/angular-rjcfgg?file=src%2Fapp%2Ftest%2Ftest.component.html

Comment: @Eliseo. Yes this works but it creates errors in the log. Your example does too.

Comment: @maxime1992, NO, you needn't use value accessor if you don't want create a custom Form Control. Just pass the control

Comment: in parent `` and in app-text, use `...`

Comment: @maxime1992, personally I don't like create a formGroup in the "children component". For me the main.component should be who create the whole form. Then pass as `@Input()` the formControl or the formGroup. You needn't nothing more, I forked your stackblitz https://stackblitz.com/edit/so-question-angular-2-large-scale-application-forms-hand-yvmtbv?file=src%2Fapp%2Fapp.component.html It's only an opinion, but, for me, it's more simple and robust and allow check only 'onblur', e.g. NOTE: If you decided use a template form, should be a great, great reason for this :)

Comment: @Sun, if the question if for me, my answer is https://stackblitz.com/edit/angular-n2vqsn?file=src/app/test/test.component.html. See that you use formControl and formGroup. I'm not very agree with maxime

Comment: 75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0glups75b5:f2c1:f5d7:b954:8dc3:cee9:baf:a8c0, see https://stackblitz.com/edit/angular-6hnbqm?file=src/app/test/test.component.ts. It's look like Angular don't like use [formControl] as parameter. Just I changed the name of the parameter [formControl]  by [control] and work

Comment: You need to use a custom value accessor for this. If you want to avoid the boilerplate you can just use https://github.com/cloudnc/ngx-sub-form see a similar post here https://stackoverflow.com/questions/40806779/angular-2-large-scale-application-forms-handling/56375605#56375605

Comment: @Eliseo I know I don't **need** to. Just like we don't need a service to host data. We could store everything into a global variable and that would save us from declaring and injecting a service, right `¯\_(ツ)_/¯`? `ControlValueAccessor` has been made to solve that use case. See https://angular.io/api/forms/ControlValueAccessor: "Defines an interface that acts as a bridge between the Angular forms API and a native element in the DOM". If you later decide to use a template form, how would you do it by passing the formControl? It's against the principles of the one way data flow too...

Comment: ... as the child will mutate a value coming from a parent instead of emitting a change event and let the parent know/handle that. Same if you pass a formGroup or formArray. You shouldn't use that. Use ControlValueAccessor with object, it's working great.


Here is another answer: You need to pass the form control to the input element ,
<input
[value]="val"
type="text"
(input)="val=$event.target.value;onChange($event.target.value)"
(blur)="onTouched()"
[formControl]="control"
&gt;
<span *ngIf="control &amp;&amp; !control.valid &amp;&amp; control.touched"&gt;
<span class="error" *ngIf="control.errors['required']"&gt; The field should not be empty</span&gt;
<span class="error" *ngIf="control.errors['email']"&gt; The field should be an email
</span&gt;
</span&gt;
Get the control in your custom component as input and show the error based on this.
import { Component, OnInit, forwardRef, Input, OnChanges } from '@angular/core';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css'],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() =&gt; TestComponent), multi: true }
export class TestComponent implements ControlValueAccessor, OnChanges {
constructor() { }
propagateChange:any = () =&gt; {};
@Input() control: FormControl;
@Input('messageValue') _messageValue = 'whateves';
get messageValue() {
return this._messageValue;
set messageValue(val) {
console.log('set messageValue', val)
this._messageValue = val;
this.propagateChange(val);
hi(event) {
console.log('hi');
console.log(event)
this.messageValue = event;
ngOnInit() {
ngOnChanges(changes) {
console.log('changes', changes);
this.propagateChange(this.messageValue);
writeValue(value) {
console.log('writeValue', value);
if (value) {
this.messageValue = value;
}
registerOnChange(fn) {
console.log('onChange')
this.propagateChange = fn;
registerOnTouched() {}
Comment for this answer: OK, I nearly have this working but I'm still struggling the errors part. How do I access the formControl to check for errors? I tried your solution but it didn't work. see this https://stackblitz.com/edit/angular-rjcfgg?file=src%2Fapp%2Ftest%2Ftest.component.html
Comment for this answer: You need'nt extends from DefaultValueAccesor nor provide a NG_VALUE_ACCESOR. if you use `[formControl]="control"` and in parent you pass as Input `` is ok
Comment for this answer: If the solution resolves your issue, you can accept this as answer.

Here is another answer: If you want access to the formControl the best thing to use is NgControl as a DI
Reasoning:
NgModel
FormControlDirective
FormControlName
Are all sub-classes of NgControl so you will be doing yourself a great deal of help if you did it this way so that if down the line you change your mind about using formControl to NgControl etc... you will have already covered those bases by using NgControl
So by example it would look something like
import { Component, OnInit, Self } from '@angular/core';
import { NgControl } from '@angular/forms';
@Component({
selector: 'app-text',
templateUrl: './text-box.component.html',
styleUrls: ['./text-box.component.css']
export class TextBoxComponent implements OnInit {
constructor(@Self() private ngControl: NgControl) { }
ngOnInit() {
console.log(this.ngControl);
<app-text[formControl]="control"&gt;</app-text&gt;
The reason we use @Self is so that the component does not look further up the injector tree to find a ngControl but only on it's element. So you can nest etc...
This is very beneficial to directives as well but so far i hope this helped!
Comment for this answer: How do I set the formControlName in the parent?

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 2, 'CommentCount': 13, 'ContentLicense': 'CC BY-SA 4.0', 'CreationDate': '2019:00:06/13/19', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<angular><angular-reactive-forms><angular-components><controlvalueaccessor>', 'Title': 'Passing formControlName into Angular component', 'ViewCount': 4566, 'id': 56580902, 'user_AboutMe': '', 'user_AccountId': 1149688.0, 'user_DisplayName': 'Sun', 'user_Reputation': 4030.0, 'user_Views': 340.0, 'user_id': 1131657.0}}