Angular <select> 绑定到对象确认更改或恢复到以前的值

Angular <select> Bound to Object Confirm Change or Revert to the Previous Value

我正在尝试实现非常简单的 <select> 行为:如果用户取消更改,则恢复到以前的值。实际上,我成功了,但是那花了我几个小时,而且我对实现仍然不满意,因为它不是那么明显和棘手。

所以,这是模板:

<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>

和组件:

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);
    }
  }
}

我希望评论是不言自明的,至少我试着把它们写成这样。

正如我在评论中提到的,简单的 this.selectedState = previousState; 不起作用,并且省略了 setTimeout()。我也试过这个,但没有成功:

this.selectedState = previousState;
this.changeDetectorRef.detectChanges();

我的解决方案也是基于 , and yes, I saw ,但它对我不起作用,因为我将 <select> 绑定到一个对象,而不是标量值。

演示: https://angular-hkaznb.stackblitz.io

软件包版本:

Angular: 6.0.0

看来我找到了更优雅的解决方案,它基于 。这个想法是操纵<select>selectedIndex。所以,这是模板:

<form class="form-inline mt-3">
    <div class="container-fluid">
        <div class="row">
            <div class="form-group col-12">
                <label for="states" class="mr-3">State</label>
                <select #s id="states" class="form-control" name="states" [ngModel]="selectedState"
                        (ngModelChange)="onStateChange(selectedState, $event, s)">
                    <option [ngValue]="null">All</option>
                    <option *ngFor="let state of states" [ngValue]="state">{{state.name}}</option>
                </select>
            </div>
        </div>
    </div>
</form>

和组件:

onStateChange(previousState: State, state: State, statesEl: HTMLSelectElement): 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 {
    statesEl.selectedIndex = this.states.indexOf(previousState) + 1;
  }
}

+1 是必需的,因为 null 不在 this.states 之列,同时是 <select> 的第一个 <option>

演示: https://angular-rwnksz.stackblitz.io