【问题标题】:Angular2+ & NgbDatePicker: proper ControlValueAccessor for Date ngModelAngular2+ 和 NgbDatePicker:Date ngModel 的正确 ControlValueAccessor
【发布时间】: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 解决方案,但还没有还能想到一个。)

Plunker link

我的应用代码:

@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


【解决方案1】:

问题在于这段代码:

writeValue(value: Date){
    if(value === null) {
        value = new Date(Date.now());
    }
    if(value.getTime() !== this.innerValue.getTime()){
        this.innerValue = value; //<-- Right here
        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.innervalue 分配给通过引用传入的确切对象!因此,当您执行 if(v.getTime() !== this.innerValue.getTime()) 时,它将始终相等,因为它是完全相同的 Date 对象。要纠正所有你需要做的是:

this.innerValue = new Date(value);

创建一个副本。

【讨论】:

    猜你喜欢
    • 2016-10-03
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 2016-11-20
    • 2021-06-02
    • 2017-01-16
    • 2017-12-21
    • 2016-11-07
    相关资源
    最近更新 更多