设置 ng-bootstrap 字段的默认值(ngModels 不能使用 getter/setter)
Setting the default value of an ng-bootstrap field (ngModels can't use getter/setter)
我在我的一个表单中使用 ng-boostrap datepicker 字段,我想用默认值初始化它。
在 "standard" 表格中,我可以使用 formControlName
或 ngModel
并且效果很好:
<!-- formControlName -->
<input ngbDatepicker #d="ngbDatepicker" formControlName="dateFieldName">
<!-- ngModel -->
<input ngbDatepicker #d="ngbDatepicker" [(ngModel)]="myDateProp">
我的问题是我将日期选择器包装在我自己的自定义字段中,即实现 ControlValueAccessor
.
的自定义组件
在这种情况下,日期选择器不再知道其父表单和相应的模型。那么如何初始化日期选择器字段呢?
formControlName
不是一个选项(我的自定义字段组件没有也不应该访问其父表单的模型)。
ngModel
似乎更容易,因为它只是绑定到本地 属性,但是一旦我将 [(ngModel)]
指令添加到日期选择器字段,我的整个表单就会冻结。
到目前为止,这是我的自定义字段组件的代码:
@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 属性 + 使用上面的语法修复了它。)
我在我的一个表单中使用 ng-boostrap datepicker 字段,我想用默认值初始化它。
在 "standard" 表格中,我可以使用 formControlName
或 ngModel
并且效果很好:
<!-- formControlName -->
<input ngbDatepicker #d="ngbDatepicker" formControlName="dateFieldName">
<!-- ngModel -->
<input ngbDatepicker #d="ngbDatepicker" [(ngModel)]="myDateProp">
我的问题是我将日期选择器包装在我自己的自定义字段中,即实现 ControlValueAccessor
.
在这种情况下,日期选择器不再知道其父表单和相应的模型。那么如何初始化日期选择器字段呢?
formControlName
不是一个选项(我的自定义字段组件没有也不应该访问其父表单的模型)。ngModel
似乎更容易,因为它只是绑定到本地 属性,但是一旦我将[(ngModel)]
指令添加到日期选择器字段,我的整个表单就会冻结。
到目前为止,这是我的自定义字段组件的代码:
@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:
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 属性 + 使用上面的语法修复了它。)