I'm trying to achieve pretty simple <select>
behavior: revert to the previous value, if user canceled the change. Actually, I succeeded, but that cost me few hours, and I'm still unhappy with the implementation, because it's not so obvious and tricky.
So, here's the template:
<select id="states" class="form-control" name="states" [ngModel]="selectedState"
(ngModelChange)="onStateChange(selectedState, $event)">
<option [ngValue]="null">All</option>
<option *ngFor="let state of states" [ngValue]="state">{{state.name}}</option>
</select>
and component:
export class AppComponent {
selectedState: State = null;
states: State[] = [
{ name: 'Alabama', population: 100000 },
{ name: 'Alaska', population: 50000 }
];
onStateChange(previousState: State, state: State): void {
// If we're changing state from "All" to any, it's OK
if (previousState === null) {
this.selectedState = state;
return;
}
// Otherwise we want the user to confirm that change
if (confirm('Are you sure you want to select another state?')) {
this.selectedState = state;
} else {
// But instead of just `this.selectedState = previousState;`
// I need to proceed with below dirty hack.
// Step 1: Changing property value, which is bound to `[ngModel]`,
// to any, except for the current one, so `null` is OK
this.selectedState = null;
// Step 2: Reverting this property value to the previous one,
// which is, ridiculously, already set as the previous one,
// because we're reverting `this.selectedState`,
// while passing exactly the same `this.selectedState`
// to this method as a `previousState` parameter,
// so we're actually doing `this.selectedState = this.selectedState;`,
// but in not-so-obvious form.
// This works only kind of asynchronously,
// after call stack is clear.
setTimeout(() => {
this.selectedState = previousState;
}, 0);
}
}
}
I hope that comments are self-explanatory, at least I tried to write them so they be such.
As I already mentioned in comments, simple this.selectedState = previousState;
doesn't work, as well as omitting the setTimeout()
. I also tried this, but got no luck:
this.selectedState = previousState;
this.changeDetectorRef.detectChanges();
My solution is based on this answer, and yes, I saw this one as well, but it just doesn't work for me, because I'm binding <select>
to an object, not scalar value.
Demo: https://angular-hkaznb.stackblitz.io
Package versions:
Angular: 6.0.0