【发布时间】:2017-10-11 06:41:41
【问题描述】:
正在为ng-bootstrap 开发一些自定义包装器。有问题的想法是创建一个将 Date 作为其 [ngModel] 的组件。
我注意到我的包装器在我正在开发的应用程序中实现它时存在一个错误,该错误需要同一对象的多个组件但在同一页面上具有不同的引用。似乎如果我创建 2 个应该引用同一个对象的变量,它们实际上会彼此不同步。例如:
this.date: Date = new Date();
this.copy: Date = date;
....
<my-component [ngModel]="date"></my-component>
<my-component [ngModel]="copy"></my-component>
<-- Components don't stay in sync - date and copy point at different Dates -->
我知道这是因为我的 ControlValueAccessor 实现有问题。不知何故,我覆盖了指向新 Date 对象的引用,而不是修改给定的对象。
我相信这是由我的 plunker 上的第 56 行引起的?但也可能与我没有正确理解/理解的 ControlValueAccessor 有关。似乎太像调整我的 Date 对象以匹配通过 DoCheck 生命周期钩子的包装的 NgbDatePicker 的 NgbDateStruct 有点hacky(尽管我认为它不会导致问题 - 更喜欢聪明的 RxJS 解决方案,但还没有还能想到一个。)
我的应用代码:
@Component({
selector: 'my-app',
template: `
<h1>
{{title}}
</h1>
<hr>
<h2>Day Picker</h2>
<app-day-picker [(ngModel)]="dayPickerDay"></app-day-picker>
Day: {{ dayPickerDay | date:mediumDate }}
<br>
<button (click)="setToYesterday()">Change to yesterday</button>
<button (click)="setToTomorrow()">Change to tomorrow</button>
<hr>
<h2>Day Picker for copy (should stay in sync... doesn't.</h2>
<app-day-picker [(ngModel)]="dayPickerCopy"></app-day-picker>
Day: {{ dayPickerCopy | date:mediumDate }}
<br>`
})
export class App {
title = 'My time components';
dayPickerDay: Date;
dayPickerCopy: Date;
ngOnInit(){
this.dayPickerDay = new Date(Date.now());
this.dayPickerCopy = this.dayPickerDay;
}
setToTomorrow(){
this.dayPickerDay = new Date(Date.now() + 24*1000*60*60);
}
setToYesterday(){
this.dayPickerDay = new Date(Date.now() - 24*1000*60*60);
}
}
我的包装代码:
const noop = () => {};
export const DAY_PICKER_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DayPickerComponent),
multi: true
};
@Component({
selector: 'app-day-picker',
template: `
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<input class="form-control" placeholder="Select a Date..."
name="dp" [(ngModel)]="dayObject" ngbDatepicker #d="ngbDatepicker" disabled="">
<button class="input-group-addon" (click)="d.toggle()" type="button">
<i class="fa fa-calendar" style="width: 1.2rem; height: 1rem; cursor: pointer;"></i>
</button>
</div>
</div>
</form>
`,
providers: [
DAY_PICKER_CONTROL_VALUE_ACCESSOR
]
})
export class DayPickerComponent implements ControlValueAccessor, DoCheck {
private innerValue: Date = new Date(Date.now());
private dayObject: {year: number, month: number, day: number};
private onTouchedCallback: () => void = noop;
private onChangedCallback: (_: any) => void = noop;
get value(): Date {
return this.innerValue;
}
set value(v: Date){
if(v.getTime() !== this.innerValue.getTime()){
this.innerValue = v;
let years = this.innerValue.getFullYear();
let months = this.innerValue.getMonth() + 1;
let days = this.innerValue.getDate();
this.dayObject = {year: years, month: months, day: days};
this.onChangedCallback(v);
}
}
writeValue(value: Date){
if(value === null) {
value = new Date(Date.now());
}
if(value.getTime() !== this.innerValue.getTime()){
this.innerValue = value;
let years = this.innerValue.getFullYear();
let months = this.innerValue.getMonth() + 1;
let days = this.innerValue.getDate();
this.dayObject = {year: years, month: months, day: days};
}
}
registerOnChange(fn: (_: any) => void): void{
this.onChangedCallback = fn;
}
registerOnTouched(fn: any){
this.onTouchedCallback = fn;
}
ngDoCheck() {
if (this.dayObject) {
this.value.setFullYear(this.dayObject.year, this.dayObject.month - 1, this.dayObject.day);
}
}
}
【问题讨论】:
-
我正在寻找相同的解决方法,你有吗?
-
这是我最终的结果:github.com/ZackDeRose/my-time-components;我们已经转移到一个哑组件,它接受一个不可变作为输入并通过 EventEmitter 输出新值;所以放弃了 ngModel 方法
标签: angular date ng-bootstrap