问题
I have an angular app. And I'm using cascading combobox (country-state) in below example. But, get states()
method in state.component.ts
running a lot of time. What can be the reason of this? I want run only country selection changed. I put debugger
. You can reproduce bug by open console with F12. If my method mistake, I can change completely my way.
Stackblitz
回答1:
When you use a getter, this happens several times alog life of component.
You must change your aproach. One way is subscribe to valueChanges of the form, and change "states" in the subscribe. Some like
export class StateComponent {
_studentForm; //Use a _studentForm
@Input()
set studentForm(value) //use a setter, to subscribe when has value
{
this._studentForm=value;
this._studentForm.get(this.countryId).valueChanges.subscribe(res=>{
var val = this._studentForm.controls[this.countryId].value;
this.states=this.selectService.filterStates(val);
})
}
@Input() id:string;
@Input() countryId:string;
states: State[];
constructor(private selectService: SelectService) { }
}
You component.html must be reference to _studentForm
<form [formGroup]="_studentForm">
<select [formControlName]="id" >
<option [value]="0">--Select--</option>
<option *ngFor="let state of states " value= {{state.id}}>{{state.name}}</option>
</select>
</form>
Your forked stackblitz
Update Well, with the vision of the whole problem, perhafs it's time to create a Component that control country and states at time. it's some more complex beacuse we must use viewProviders and FormGroupDirective.
The idea, pass as argument to the component the name of the controls (countryID and StateID) and the label of the controls (countryLabel and stateLabel)
The new component becomes as
@Component({
selector: 'app-country-state',
viewProviders: [
{
provide: ControlContainer,
useExisting: FormGroupDirective
}
],
templateUrl: './country-state.component.html',
styleUrls: ['./country-state.component.css']
})
export class CountryStateComponent implements OnInit, OnDestroy {
@Input() countryID: string;
@Input() stateID: string;
@Input() countryLabel: string;
@Input() stateLabel: string;
_countryID: FormControl; //We must control two "FormControl"
_stateID: FormControl; //One for country and one for stated
states: any[] = [];
countries: any[] = [];
isAlive: boolean = true;
constructor(private selectService: SelectService,
private fgd: FormGroupDirective) { }
ngOnInit() {
//first, we get the countries
this.countries = this.selectService.getCountries();
//"search" the controls using the FormGroupDirective
this._countryID = (this.fgd.form.get(this.countryID) as FormControl);
this._stateID = (this.fgd.form.get(this.stateID) as FormControl);
//Our subscribe to valueChanges. We use a "tipical" contruction "takeWhile"
//To unsubscribe when the compnent are destroyed
this._countryID.valueChanges.pipe(
takeWhile(() => this.isAlive)
).subscribe(res => {
this.states = this.selectService.filterStates(this._countryID.value);
})
}
ngOnDestroy() {
this.isAlive = false;
}
}
The .html
{{countryLabel}}:<br/>
<!--see that we use [formControl], NOT [formControlName]-->
<select [formControl]="_countryID">
<option [value]="0">--Select--</option>
<option *ngFor="let country of countries" [value]="country.id">{{country.name}}</option>
</select>
<br/>
{{stateLabel}}:<br/>
<select [formControl]="_stateID" >
<option [value]="0">--Select--</option>
<option *ngFor="let state of states " value= {{state.id}}>{{state.name}}</option>
</select>
And the use
<app-country-state
[countryID]="'countryId1'" [countryLabel]="'Student Country'"
[stateID]="'stateId1'" [stateLabel]="'Student State'">
</app-country-state>
The forked stackblitz
来源:https://stackoverflow.com/questions/54073280/cascading-dropdown-fill-method-running-more-than-one-in-angular-7