在 Angular 中使日期字段具有反应性

Making a date field reactive in Angular

我正在尝试从 ngx-bootstrap 自定义日期字段制作一个 datepicker 组件,以便我可以全球化一些功能和配置。但是我似乎无法在日期输入字段中捕获 Date 对象的值。

我的 date-field.ts(我正在重新使用文本字段中的一些设置。如果您看到文本字段组件的一些残余,请耐心等待。但我确定我的主要问题是我的组件不知道它是一个日期字段)

  import { Component, OnInit, forwardRef, Input } from '@angular/core';
  import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

  @Component({
    selector: 'date-field',
    templateUrl: './date-field.component.html',
    styleUrls: ['./date-field.component.scss'],
    providers:[
      {
        provide: NG_VALUE_ACCESSOR, 
        multi: true,
        useExisting: forwardRef(() => DateFieldComponent),
      }
    ]
  })
  
  export class DateFieldComponent implements OnInit, ControlValueAccessor {

    public dateField = new FormControl("")
    private onChange: (name: string) => void;
    private onTouched: () => void

    @Input() name: string;
    @Input() label: string;
    @Input() required: boolean;

    datepickerConfig = {
      dateInputFormat: 'ddd, MMMM Do YYYY', 
      isAnimated: true, 
      adaptivePosition: true, 
      returnFocusToInput: true, 
      containerClass: 'theme-dark-blue'
    } 

    constructor() { }

    ngOnInit() { }


    writeValue(obj: any): void {
      const date = Date
      this.dateField.setValue(new Date());
      console.log(date);
    }

    registerOnChange(fn: any): void {
      this.onChange = fn;
    }
    registerOnTouched(fn: any): void {
      this.onTouched = fn;
    }

    setDisabledState?(isDisabled: boolean): void {
      if (isDisabled) {
        this.dateField.disable();
      } else {
        this.dateField.enable();
      }
    }

    doInput() {
      this.onChange(this.dateField.value)    
    }

    doBlur() {
      this.onTouched();
    }

  }

模板HTML:

  <label
    *ngIf="label"
    for="{{name}}"
    class="col-auto col-form-label {{required ? 'required' : '' }}">
    {{label}}
  </label>
  <div class="col-expand relative">
    <input 
      type="text"
      class="form-control date-field"
      #dp="bsDatepicker"
      [formControl]="dateField"
      (input)="doInput()"
      (blur)="doBlur()"
      ngModel
      bsDatepicker
      [bsConfig]="datepickerConfig"
      required="{{required}}">  
  </div>

像这样在父表单中使用它:

 <date-field
     name="dateChartered"
     label="Date local union chartered"
     formControlName="dateChartered"
     required="true">
 </date-field>

 <p><strong>Date chartered is:</strong> {{dateChartered}}</p>

假设您在父组件中正确地在控制器中初始化 FormGroup 并在模板中正确使用它,您的组件中有两个主要错误。

首先,正如 Belle Zaid 所说,您应该从自定义日期选择器的 <input>.

中删除 ngModel

其次,您将 doInput() 绑定到 (input),但只有当您在输入字段中键入时才会触发,(change) 也是如此。您应该绑定到 (bsValueChange) 这是 BsDatepicker 公开的输出事件,它更安全,除非您计划更新用户输入的值。

生成的模板将如下所示:

<label *ngIf="label"
       for="my-custom-datepicker"
       class="col-auto col-form-label {{required ? 'required' : '' }}">

  {{label}}

</label>


<div class="col-expand relative">
  <input id="my-custom-datepicker"
         type="text"
         class="form-control date-field"
         required="{{required}}"
         bsDatepicker
         [formControl]="dateField"
         [bsConfig]="datepickerConfig"
         (blur)="doBlur()"
         (bsValueChange)="doInput()">  
</div>

完成这两项更改后,您会注意到控制台中出现错误:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value for 'ng-pristine': 'true'. Current value: 'false'.. Find more at https://angular.io/errors/NG0100

这是因为在 writeValue 中调用 this.dateField.setValue(obj) 也会触发 (bsValueChange),因此 doInput()。要解决此问题,您可以按如下方式编辑代码:


private componentInit = false;

// ...

doInput() {
  if (!this.componentInit) {
    this.componentInit = true;
    return;
  }

  this.onChange(this.dateField.value);
}