Reputation: 1189
I have an angular 2 reactive form with four formControl
s and only one input field. What I want is to ask the user to fill up the infos one by one. So I'm assigning the firstControlName
to a property call currentFormControlName
on ngOnInit
and binding it with the input field in template file. When the user fills up his name the field will be valid and on submit I'll change the currentFormControlName
property to next formControlName. But the problem is the binding isn't updating. The input field is still bound to name
. When I type something on the input field the value of name
is updating, not email
.
app.component.ts
ngOnInit() {
this.form = this.builder.group({
'name': ['', Validator.required],
'email': ['', Validator.email],
'phone': ['', Validator.required],
'password': ['', Validator.required],
});
this.currentFormControlName = 'name';
}
submit() {
this.currentFormControlName = 'email'; // Setting it manually just for the demo of this question.
}
app.component.html
<form [formGroup]="form">
<input type="text" [formControlName]="currentFormControlName">
<input type="submit" (click)="submit()">
</form>
Upvotes: 3
Views: 22546
Reputation: 71
You can use this Approach: Simply combine specific control with form-control because at the end formControlName also resolves into FormControl
<form [formGroup]="form" #formDir="ngForm">
<input type="text" [formControl]="currentFormControl">
<input type="submit" (click)="submit()">
{{ form.value | json }}
in TS
currentFormControl: FormControl;
ngOnInit(){
this.currentFormControl = this.form.controls.name;
}
submit(){
this.currentFormControl = this.form.controls.email;
}
Upvotes: 0
Reputation: 214017
Update
You can also use FormControlDirective
to switch between controls
[formControl]="form.get(currentFormControlName)"
Old Answer
Let's say we have the following
template.html
<form [formGroup]="form" #formDir="ngForm">
<input type="text" #controlDir [formControlName]="currentFormControlName">
<input type="submit" (click)="submit()">
</form>
<pre>{{ form.value | json }}</pre>
After clicking on submit button we can change currentFormControlName
and register control with new name like
component.ts
form: FormGroup;
@ViewChild('formDir') formDir: FormGroupDirective;
@ViewChild('controlDir', { read: FormControlName }) controlDir: FormControlName;
currentFormControlName: string;
constructor(private builder: FormBuilder) {}
ngOnInit() {
this.form = this.builder.group({
'name': ['', Validators.required],
'email': ['', Validators.email],
'phone': ['', Validators.required],
'password': ['', Validators.required],
});
this.currentFormControlName = 'name';
}
submit() {
this.formDir.removeControl(this.controlDir);
this.controlDir.name = this.currentFormControlName = 'email'
this.formDir.addControl(this.controlDir);
}
After that our input element will manage email
value. So if we type something in input
it will be reflected in form.email
value
This solution is based on FormControlName source code
ngOnChanges(changes: SimpleChanges) {
if (!this._added) this._setUpControl();
if (isPropertyUpdated(changes, this.viewModel)) {
this.viewModel = this.model;
this.formDirective.updateModel(this, this.model);
}
}
we can see this directive registers control only once. But it also have the following ngOnDestroy hook
ngOnDestroy(): void {
if (this.formDirective) {
this.formDirective.removeControl(this);
}
}
that gave me some idea
Upvotes: 11
Reputation: 105449
You can't do that because ngFormControlName
directive uses @Input() name
only once here:
@Directive({selector: '[formControlName]'...})
export class FormControlName extends ... {
@Input('formControlName') name: string;
ngOnChanges(changes: SimpleChanges) {
if (!this._added) this._setUpControl(); <------------ here
if (isPropertyUpdated(changes, this.viewModel)) {
On each subsequent change this._added
will be true
. The next check you can see in ngOnChanges
is isPropertyUpdated
which doesn't check name
input change and only checks model
input:
export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {
if (!changes.hasOwnProperty('model')) return false;
const change = changes['model'];
To do what you're trying to do you need to use ngFormDirective
which does check that control is updated:
export class FormControlDirective extends NgControl implements OnChanges {
viewModel: any;
@Input('formControl') form: FormControl;
ngOnChanges(changes: SimpleChanges): void {
if (this._isControlChanged(changes)) {
private _isControlChanged(changes: {[key: string]: any}): boolean {
return changes.hasOwnProperty('form');
}
However, this is a standalone directive.
Upvotes: 2