Reputation: 67
I need some advices to realise a form in my angular2 application.
I have a situation object who contains three importants things : id, name, type In a form, I create a dropdown list with the situations name (I use a service to get the list and use a ngFor directive to display) According to the type of the selected situation, I build a formGroup with those own formControls (I use an onChange function on the dropdown list) I use the type to display the new inputs in the template (many situations can have the same type). I have a problem in my template with this code, the selected value doesn't appear anymore when I select a value ...
Below, a snippet of my code to understant what I'm talking about :
Template:
<form [formGroup]="myForm" (ngSubmit)="saveForm()" novalidate>
<select class="form-control" [(ngModel)]="selectedSituation" (change)="onChangeSituation()" formControlName="name">
<option [ngValue]="situation" *ngFor="let situation of situationsArray">{{situation.name}}</option>
</select>
<div *ngIf="selectedSituation?.type == 1">
<!-- some fields -->
</div>
<div *ngIf="selectedSituation?.type == 2">
<!-- other fields -->
</div>
</form>
Component:
situationsArray: string[];
selectedSituation: any;
type: any;
constructor(
private _fb: FormBuilder
) { }
createDefaultForm(){
this.myForm = this._fb.group({
name: ['', Validators.required]
// inputs not concerning by dropdown list
});
}
createType1Form(){
this.myForm = this._fb.group({
name: ['', Validators.required]
// inputs for type = 1
});
}
createType2Form(){
this.myForm = this._fb.group({
name: ['', Validators.required]
// inputs for type = 2
});
}
ngOnInit(){
this.createDefaultForm(); // a formGroup for the inputs who aren't concerned by the dropdwon list
this.getListSituations(); // a service to get the situations list
}
onChangeSituation(): void{
console.log(this.selectedSituation);
if(this.selectedSituation.type == 1) {
this.createJobForm();
} else
if(this.selectedSituation.type == 2) {
this.createPeForm();
} else
if(this.selectedSituation.type == 3) {
this.createPeaForm();
} else
this.createDefaultForm();
}
Do you have any idea concerning the display problem ? Maybe I use a wrong practice ...
I'm stuck for a while now, all your advices will be welcome, Thx.
Upvotes: 1
Views: 858
Reputation: 73357
Problems:
You are resetting the name
form control when you build your form(s)
You are using an object as value for the select. Even if you didn't have the above problem, Angular cannot match object values without reference to each other.
Secondly, like mentioned in other answer, do not mix template driven and reactive forms, they really don't go good together.
What I would do is to change the select, to take for example the type
you are looking for, and then create a sub form group for the conditional fields. That way you can just toggle the subgroups and not needing to rebuild all common (if any other) fields. So something like.... (shortened code)
createDefaultForm() {
this.myForm = this._fb.group({
name: ['', Validators.required],
// other inputs
subGroup: this._fb.group({})
});
}
Your select which toggles the sub group:
<select class="form-control" (change)="createSubForm()" formControlName="name">
<option [ngValue]="situation.type" *ngFor="let situation of situationsArray">
{{situation.name}}
</option>
And then the method createSubGroup
:
createSubForm() {
if (this.myForm.controls.name.value === 1) {
this.myForm.setControl('subGroup', this.createType1Form())
} else {
this.myForm.setControl('subGroup', this.createType2Form())
}
}
createType1Form() {
return this._fb.group({
name1: ['type1', Validators.required]
// inputs for type = 1
});
}
createType2Form() {
return this._fb.group({
name2: ['type2', Validators.required]
// inputs for type = 2
});
}
A StackBlitz showcasing this: DEMO
Upvotes: 2
Reputation: 5552
You are mixing reactive-driven form building with template-driven form building.
The directive "[(ngModel)]" is intended for template-driven forms.
You need to change the template to this:
<select class="form-control" formControlName="name">
<option [value]="situation.type" *ngFor="let situation of situationsArray">
{{situation.name}}
</option>
</select>
This will display situation.name's in the dropdown, and when one is selected, the value of the formControl
will be set to situation.type
.
In your ngOnInit:
this.myForm.controls['name'].valueChanges.subscribe(
value => {
console.log(value);
onChangeSituation(value);
}
);
Then change your onChangeSituation() to:
onChangeSituation(newValue: number): void{
switch(newValue) {
case 1:
this.createJobForm();
break;
case 2:
this.createPeForm();
break;
case 3:
this.createPeaForm();
break;
default:
this.createDefaultForm();
break;
}
}
I have assumed that newValue is a number.
EDIT
With this solution however, since you create a subscription to the valueChanges of the form control, you need to unsubscribe when destroying the component. There are several ways to achieve that. The problem is described here.
The recommended solutions are described here. So I would do this:
In your component add a new variable (a Subject):
destroy$: Subject<boolean> = new Subject<boolean>();
Modify the subscription line to:
this.myForm.controls['name'].valueChanges.takeUntil(this.destroy$).subscribe(
Add ngOnDestroy method:
ngOnDestroy() {
this.destroy$.next(true);
// Now let's also unsubscribe from the subject itself:
this.destroy$.unsubscribe();
}
and modify the signature of your component class to implement it:
export class MyComponent implements OnInit, OnDestroy
There is another solution here for destroying subscriptions and more here - I advise reading the comments and the various answers there.
Upvotes: 0
Reputation: 2274
I think the problem is because the is inside the form. Everytime you are building a new form you are generating a new select, but this has no value. Try moving the select out
Upvotes: 0