Компонент выбора угловых материалов - PullRequest
0 голосов
/ 07 июня 2018

Пример Stackblitz

Я хотел бы создать компонент, который обернут вокруг реализации выбора даты материала, чтобы упростить использование компонента даты во всем приложении.В какой-то момент это работало, но я смешивал код на основе шаблонов и реактивных форм, который никогда не был хорошей идеей, а теперь выдает предупреждения, что он устарел.Поэтому я пытаюсь переписать, используя только ngModel.Однако я не могу заставить валид работать правильно при начальной загрузке.Всегда выдает обязательную ошибку поля.

HTML

 <mat-form-field style="width:inherit">
  <input id="DatePickerControlInput" name="datePickerControlInput" #aocDatePickerControl="ngModel" (ngModelChange)="change($event)"
         matInput [matDatepicker]="picker" [required]="required"
         [placeholder]="label" [(ngModel)]="innerValue" (blur)="onBlur($event)" (dateChange)="onDateChange($event)" [max]="maxDate">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker></mat-datepicker>
  <mat-error *ngIf="aocDatePickerControl.invalid && (aocDatePickerControl.dirty || aocDatePickerControl.touched)">
    <div *ngIf="aocDatePickerControl.errors.required && !aocDatePickerControl.errors.matDatepickerParse">Required field</div>
    <div *ngIf="aocDatePickerControl.errors.matDatepickerMax">Date cannot be in the future</div>
    <div *ngIf="aocDatePickerControl.errors.matDatepickerParse">Not a valid date</div>
  </mat-error>
</mat-form-field>

TypeScript

import { Component, OnInit, OnChanges, forwardRef, HostBinding, Input, ViewChild, ElementRef } from '@angular/core';
import {
  ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl,
  Validator, Validators, ValidatorFn, AbstractControl, NgModel
} from '@angular/forms';
import * as moment from 'moment';

@Component({
  selector: 'aoc-date-picker',
  templateUrl: './CustomDatePicker.component.html',
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => AocDatePickerComponent),
      multi: true
    },
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => AocDatePickerComponent),
      multi: true
    }
  ]
})
export class AocDatePickerComponent implements OnInit, OnChanges, ControlValueAccessor, Validator {

  @ViewChild(NgModel) aocDatePickerControl: NgModel;
  @ViewChild('aocDatePickerControl', { read: ElementRef }) inputControl: ElementRef;

  @Input() label = 'Choose a date';
  @Input() required?= false;
  @Input() disabled?= false;
  @Input() autofocus?= false;
  @Input() allowFutureDates?= true;

  maxDate;

  innerValue;

  onChange: any = () => { };
  onTouched: any = () => { };

  constructor() { }

  focus() {
    this.inputControl.nativeElement.focus();
  }

  ngOnInit() {
  }

  ngOnChanges() {
    this.maxDate = this.allowFutureDates ? null : new Date();
  }

  writeValue(value: any) {
    this.innerValue = value;

    // this.aocDatePickerControl.control.setValue(this.innerValue);
    // this.aocDatePickerControl.control.updateValueAndValidity();
    // this.aocDatePickerControl.model = value;
    // this.aocDatePickerControl.valueAccessor.writeValue(value);
  }

  // This needs to be wired to the dateChange event and not dateInput event so that
  // the changed value is only bubbled up when the user changes focus or selects from
  // the popup calendar and not on each key stroke
  onDateChange(event) {
    this.onChange(event.value);
  }

  registerOnChange(fn: (value: any) => void) {
    this.onChange = fn;
  }

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

  setDisabledState(isDisabled: boolean) {
    this.disabled = isDisabled;
    if (isDisabled) {
      this.aocDatePickerControl.control.disable();
    } else {
      this.aocDatePickerControl.control.enable();
    }
  }

  validate(control: FormControl) {
    const errors = Object.assign({}, this.aocDatePickerControl.errors || {});
    return Object.keys(errors).length && this.aocDatePickerControl.invalid ? errors : null;
  }

  onBlur($event) {
    if ($event.target && $event.target.value && $event.target.value.length === 8 && !isNaN($event.target.value)) {
      const val: String = $event.target.value;
      const month = val.slice(0, 2);
      const day = val.slice(2, 4);
      const year = val.slice(4);
      this.innerValue = new Date(`${ month }/${ day }/${ year }`);
      this.aocDatePickerControl.control.setValue(this.innerValue);
      this.aocDatePickerControl.control.updateValueAndValidity();
      this.onChange(this.innerValue);
    }
    if (this.aocDatePickerControl.hasError('matDatepickerParse')) {
      this.aocDatePickerControl.control.setValue(null);
      this.aocDatePickerControl.control.updateValueAndValidity();
    }

    this.onTouched();
  }
}

1 Ответ

0 голосов
/ 07 июня 2018

Я получил некоторую помощь от публикации проблемы на сайте Material Github, которая исправила мою проблему.В принципе, для чего-то подобного реактивные формы лучше.Вот комментарий и ссылка на обновленный стек-блиц.

В этом случае проще обрабатывать состояния с помощью реактивного управления: Обновлен StackBlitz

...