设置 ng-bootstrap 字段的默认值(ngModels 不能使用 getter/setter)

Setting the default value of an ng-bootstrap field (ngModels can't use getter/setter)

我在我的一个表单中使用 ng-boostrap datepicker 字段,我想用默认值初始化它。

在 "standard" 表格中,我可以使用 formControlNamengModel 并且效果很好:

<!-- formControlName -->
<input ngbDatepicker #d="ngbDatepicker" formControlName="dateFieldName">
<!-- ngModel -->
<input ngbDatepicker #d="ngbDatepicker" [(ngModel)]="myDateProp">

我的问题是我将日期选择器包装在我自己的自定义字段中,即实现 ControlValueAccessor.

的自定义组件

在这种情况下,日期选择器不再知道其父表单和相应的模型。那么如何初始化日期选择器字段呢?

到目前为止,这是我的自定义字段组件的代码:

@Component({
  selector: 'field-datetime',
  template: `
    <div class="input-group">
      <input class="form-control" ngbDatepicker #d="ngbDatepicker">
      <div class="input-group-addon" (click)="d.toggle()" >
        <i class="fa fa-calendar" style="cursor: pointer;"></i>
      </div>
    </div>`,
  providers: [
    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FieldDateTimeComponent), multi: true }
  ]
})
export class FieldDateTimeComponent implements ControlValueAccessor {
  private _dateTimeObj: DateTime;  // Date is stored as an object internally

  propagateChange = (_: any) => {};

  // Write a new value to the element (from the form model into the view).
  writeValue(timestamp: number) {
    // Parse the timestamp to store it as an object internally.
    this._dateTimeObj = moment(timestamp).toObject();
    this.propagateChange(this._dateTimeObj);
  }

  registerOnChange(fn: any) {
    this.propagateChange = fn;
  }

  registerOnTouched(fn: any) {}

  // This is the property I'd like to bind to the field, i.e.:
  // <input ngbDatepicker [(ngModel)]="dateObj">
  // THIS IS WRONG. DO NOT USE A GETTER FOR A NGMODEL. SEE ANSWER BELOW.
  get dateObj() {
    return {day: this._dateTimeObj.date, month: this._dateTimeObj.months, year: this._dateTimeObj.years};
  }

}

正确的解决方案显然是使用 [(ngModel)] 。你真正的问题是当你尝试使用它时冻结。

我认为问题在于在 writeValue 中调用 propagateChanges,这会触发无限循环。 请尝试以下操作:

html:

<input class="form-control" ngbDatepicker #d="ngbDatepicker" (change)="handleChange()" [(ngModel)]="_dateTimeObj">

ts:

writeValue(timestamp: number) {
  // Parse the timestamp to store it as an object internally.
  this._dateTimeObj = moment(timestamp).toObject();
}
handleChange($event) { 
  // manage sending the value back from the input to your model using $event.target.value
  this.propagateChange(this._dateTimeObj);
}

呃...问题是对 ngModel 使用 getter/setter(与 ng-bootstrap 无关)。

换句话说,给定模板中的这个字段:

<input name="foo" [(ngModel)]="foo">

foo 属性 不能在 class:

中使用 getter/setter
export class MyComp() {
  private _foo;
  get foo() {
    return this._foo;
  }
  set foo(val) {
    this._foo = val;
  }
}

但是为了仍然能够对模型更改做出反应(这是首先使用 getter/setter 的全部意义),我不得不重写 ngModel 指令以使用它的扩展形式:

<input name="foo" [ngModel]="foo" (ngModelChange)="onFooChanged($event)">

现在 onFooChanged() 让我 propagateChange 从我的自定义字段到父表单(就像 setter 让我这样做一样)。

(在我最初的示例中,get dateObj() 是问题所在。将其替换为标准 class 属性 + 使用上面的语法修复了它。)