Reputation: 7024
I've been trying to work with ngFormModel and dynamic properties from classes, without any luck. What happens is that the first value is bound correctly, but as soon as i change the variable to another value, the ngFormModel is not updated to reflect that. Here's a small demo with what's going on:
http://plnkr.co/edit/XnbvYtPW9BFDMcdDp0St
export class HeroForm {
protected ssn: Control;
protected address: Control;
build(): ControlGroup
{
this.ssn = new Control('', Validators.required);
this.address = new Control('');
return new ControlGroup({
ssn: this.ssn,
address: this.address
});
}
}
In this demo, each Hero can have a specific HeroForm. Each HeroForm can contain different fields and values, that why the ngFormModel has to be dynamic. In my template, i'm binding ngFormModel to the form property of the current selectedHero. To reproduce the issue, select a hero, fill the form and submit. You'll see that everything is OK in the console. But try selecting another hero: now the form is always empty.
Also, another question in the same subject and demo: how can i have dynamic templates based on each HeroForm class? For example, AnotherHeroForm contains different fields than HeroForm, so, how can i have a markup for each?
Upvotes: 3
Views: 294
Reputation: 202216
I think that you should use ngFormControl
instead ngControl
into the HeroDetailComponent
class (file app/hero-detail.component.ts
), as described below:
<form [ngFormModel]="hero.form">
<p>SSN: <input type="text"
[ngFormControl]="hero.form.controls.ssn"></p> <-------
<p>Address: <input type="text"
[ngFormControl]="hero.form.controls.address"></p> <-------
</form>
Here is the refactored plunkr: http://plnkr.co/edit/8eENXoDkcm4vYhZ0XY1H?p=preview.
NgFormControl
directive allows to configure controls you defined with components. Using interpolation (with square brackets) you will use the control references...
Edit
Following your comment, I updated my plunkr to make things dynamic.
I updated the Hero
class to contain fields we want to update:
export class Hero {
constructor(public id: number, public name: string,
public ssn: string, public address: string,
public form: ControlGroup) {
this.form = form.build();
}
}
I created instances of Hero
with values accordingly:
var HEROES: Hero[] = [
new Hero(1, "Narco", 'ssn1', 'address1', new HeroForm()),
new Hero(2, "Magma", 'ssn2', 'address2', new HeroForm()),
new Hero(3, "Tornado", 'ssn3', 'address3', new HeroForm()),
];
I updated the HeroDetailComponent
component to be dynamic, i.e. iterate over the fields of the provided hero:
@Component({
selector: 'my-hero-detail',
template: `
<div *ngIf="hero">
<h2>{{hero.name}} details!</h2>
<div *ngIf="hero">
<form [ngFormModel]="hero.form">
<div *ngFor="#elt of hero | fields">
<p>{{elt}}:
<input type="text" [(ngModel)]="hero[elt]"
[ngFormControl]="hero.form.controls[elt]">
- Valid: {{hero.form.controls[elt].valid}}
</p>
</div>
</form>
</div>
</div>
`,
pipes: [FieldsPipe]
})
export class HeroDetailComponent {
@Input()
public hero: Hero;
}
Both ngModel
and ngFormControl
are dynamically bound to the current element in the loop (for a particular field). I display the validity of the corresponding control to see that updates are taken into account...
To get the list of fields for inputs, I created a custom pipe to get and filter them:
@Pipe({
name: 'fields'
})
export class FieldsPipe {
transform(array, args: string): Array<string> {
// Get fields
var fields = [];
for (let elt in array) {
// Exclude non updatable fields (form, id and name)
if (elt!=='form' && elt!=='id' && elt!=='name') {
fields.push(elt);
}
}
return fields;
}
}
The plunkr link remains the same: http://plnkr.co/edit/8eENXoDkcm4vYhZ0XY1H?p=preview.
Upvotes: 3
Reputation: 1708
Have you tried to bind ngModel like this example ? https://angular.io/docs/ts/latest/api/common/NgFormModel-directive.html
Upvotes: 0