Working with angular 7 and Bootstrap 4, I want to wrap my bootstrap 4 inputs in a custom component in order to reduce the boilerplate in my templates.
I want that the fi
The "key" is using viewProvider. You use a @Input set to give value to a formControl, see stackblitz. The "magic" is that if equal refered to formControl in the "children" or form.get('input1') in the parent
@Component({
selector: 'app-form-control',
template: `
<div class="form-group row">
<label class="col-2 col-form-label">{{label}}</label>
<div class="col-10">
<input class="form-control" placeholder="{{placeholder}}"
[formControl]="formControl" autocomplete="nope"/>
</div>
</div>
<!--you can control the properties of formControl-->
{{formControl.valid}}{{formControl.touched}}}
`,
viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }]})
export class HelloComponent {
formControl: FormControl;
constructor(private parentF: FormGroupDirective) { }
@Input()
set controlName(value) {
this.formControl = this.parentF.form.get(value) as FormControl
}
@Input() label: string;
@Input() placeholder: string;
}
And call the component this way:
<form [formGroup]="myForm" (submit)="submit(myForm.value)">
<app-form-control label="Lastname" placeholder="Lastname" controlName="lastName"></app-form-control>
</form>
Update well, (after a year) take account the stackblitz was erroneous. when you (click) in buttons create a new Form:
this.form=this.createForm({note:'lll'})
This "break" the relationship between the component and the form because the relation is about the older form -only change if change the @Input "nameControl". So the correct is use a setValue to give a new value to the form.
You can implement by implementing ControlValueAccessor
. Lets do the step by step process by building a TextBoxComponent
.
step 1: Creating NG_VALUE_ACCESSOR
for textfield as TEXTBOX_VALUE_ACCESSOR
.
const TEXTBOX_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TextBoxComponent),
multi: true,
};
step 2: implemnting ControlValueAccessor
to our component TextBoxComponent
.
export class TextBoxComponent implements ControlValueAccessor{
...
...
}
step 3: define unimplemnted methods of ControlValueAccessor
. The detailed code of TextBoxComponent
as below.
@Component({
selector: "text-box",
template: `
<div class="form-group row">
<label class="col-2 col-form-label">{{label}}</label>
<div class="col-10">
<input class="form-control" placeholder="{{placeholder}}" [(ngModel)]="inputValue" />
</div>
</div>
`,
providers: [TEXTBOX_VALUE_ACCESSOR],
})
export class TextBoxComponent implements ControlValueAccessor {
private _inputValue: any = '';
private _onTouchedCallback: () => {};
private _onChangeCallback: (_:any) => {};
@Input("label") label: string = "Your Label";
@Input("placeholder") placeholder: string = "Your Placeholder";
get inputValue(): any {
return this._inputValue;
}
set inputValue(value: any) {
if (value !== this._inputValue) {
this._inputValue = value;
this._onChangeCallback(value);
}
this._onTouchedCallback();
}
//From ControlValueAccessor interface
writeValue(value: any) {
this._inputValue = value;
}
//From ControlValueAccessor interface
registerOnChange(fn: any) {
this._onChangeCallback = fn;
}
//From ControlValueAccessor interface
registerOnTouched(fn: any) {
this._onTouchedCallback = fn;
}
}
How to use :
<form [formGroup]="formGroup">
<text-box formControlName="textboxControl" label="My Label" placeholder="My Placeholder"></text-box>
<pre>{{formGroup.value | json}}</pre>
</form>
The complete code is available in stackblitz.