Ошибка mat не видна на компоненте, который я создал - PullRequest
0 голосов
/ 06 октября 2019

Я использую Angular 8 с угловым материалом для создания моего приложения.

У меня определено следующее поле формы:

<mat-form-field floatLabel="always">
        <app-my-datetime-input placeholder="From" formControlName="fromDatetime"></app-my-datetime-input>
        <mat-error>{{getError('fromDatetime')}}hello</mat-error>
        <mat-hint>YYYY-MM-DD HH:MM:SS</mat-hint>
      </mat-form-field>

и app-my-datetime-input - это компонент, который я создал с помощьюследующий код:

html:

<div [formGroup]="parts">
  <input  matInput mask="0000-00-00 00:00:00" formControlName="datetime" (input)="_handleInput()" />
</div>

и это машинопись:

import {Component, ElementRef, forwardRef, HostBinding, Input, OnDestroy, OnInit, Optional, Self, ViewChild} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, NgControl} from '@angular/forms';
import {MatFormFieldControl, MatInput} from '@angular/material';
import {Subject} from 'rxjs';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {FocusMonitor} from '@angular/cdk/a11y';

@Component({
  selector: 'app-my-datetime-input',
  templateUrl: './my-datetime-input.component.html',
  styleUrls: ['./my-datetime-input.component.scss'],
  providers: [{provide: MatFormFieldControl, useExisting: MyDatetimeInputComponent}],
})

export class MyDatetimeInputComponent implements ControlValueAccessor, MatFormFieldControl<string>,
              OnDestroy {

  get empty() {
    const {value: {datetime}} = this.parts;

    return !datetime;
  }
  // TODO: fix should label float
  get shouldLabelFloat() { return this.focused || !this.empty; }

  @Input()
  get placeholder(): string { return this._placeholder; }
  set placeholder(value: string) {
    this._placeholder = value;
    this.stateChanges.next();
  }

  @Input()
  get required(): boolean { return this._required; }
  set required(value: boolean) {
    this._required = coerceBooleanProperty(value);
    this.stateChanges.next();
  }

  @Input()
  get disabled(): boolean { return this._disabled; }
  set disabled(value: boolean) {
    this._disabled = coerceBooleanProperty(value);
    this._disabled ? this.parts.disable() : this.parts.enable();
    this.stateChanges.next();
  }

  @Input()
  get value(): string {
    const {value: {datetime}} = this.parts;
    return datetime;
  }
  set value(datetime: string) {
    this.parts.setValue({datetime});
    this.stateChanges.next();
  }

  constructor(
    formBuilder: FormBuilder,
    // tslint:disable-next-line:variable-name
    private _focusMonitor: FocusMonitor,
    // tslint:disable-next-line:variable-name
    private _elementRef: ElementRef<HTMLElement>,
    @Optional() @Self() public ngControl: NgControl) {

    this.parts = formBuilder.group({
      datetime: '',
    });

    _focusMonitor.monitor(_elementRef, true).subscribe(origin => {
      if (this.focused && !origin) {
        this.onTouched();
      }
      this.focused = !!origin;
      this.stateChanges.next();
    });

    if (this.ngControl != null) {
      this.ngControl.valueAccessor = this;
    }
  }
  static nextId = 0;

  parts: FormGroup;
  stateChanges = new Subject<void>();
  focused = false;
  errorState = false;
  controlType = 'my-datetime-input';
  id = `my-datetime-input-${MyDatetimeInputComponent.nextId++}`;
  describedBy = '';
  // tslint:disable-next-line:variable-name
  private _placeholder: string;
  // tslint:disable-next-line:variable-name
  private _required = false;
  // tslint:disable-next-line:variable-name
  private _disabled = false;
  onChange = (_: any) => {};
  onTouched = () => {};

  ngOnDestroy() {
    this.stateChanges.complete();
    this._focusMonitor.stopMonitoring(this._elementRef);
  }

  setDescribedByIds(ids: string[]) {
    this.describedBy = ids.join(' ');
  }

  onContainerClick(event: MouseEvent) {
    if ((event.target as Element).tagName.toLowerCase() !== 'input') {
      // tslint:disable-next-line:no-non-null-assertion
      this._elementRef.nativeElement.querySelector('input')!.focus();
    }
  }

  writeValue(val: string): void {
    this.value = val;
  }

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

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

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  _handleInput(): void {
    this.onChange(this.parts.value.datetime);
  }

}

это первый раз, когда я создаю свое собственное поле формыкомпонент, так что я, вероятно, сделал что-то не так .. mat-error в не видно. как вы можете видеть, я добавил слово hello к концу mat-error, и я до сих пор не вижу его на экране. так что я предполагаю, что я должен был реализовать MatFormFieldControl - это менее глючный способ ?! :) поэтому я не знаю, что я сделал не так, поэтому любая информация по этому вопросу будет принята с благодарностью.

спасибо

обновление

добавлено (blur)="onTouched() нок сожалению, результаты совпадают.

У меня есть проверка формы, которая гарантирует, что с даты не новее, чем на сегодняшний день. это моя функция проверки:

 static fromToRangeValidator(): ValidatorFn {
      return (group: FormGroup): ValidationErrors => {
        const fromDate = group.get('fromDatetime');
        const toDate = group.get('toDatetime');
        if (fromDate.value !== '' && toDate.value !== '') {
          const fromMoment = moment(fromDate.value, 'YYYYMMDDHHmmss');
          const toMoment = moment(toDate.value, 'YYYYMMDDHHmmss');
          if (toMoment.isBefore(fromMoment)) {
            fromDate.setErrors({greaterThen: true});
            toDate.setErrors({lessThen: true});
          }
        }
        return;
      };
    }

и форма не отправляется из-за ошибки, но ошибка по-прежнему не отображается

1 Ответ

0 голосов
/ 06 октября 2019

Ошибка матов отображается только при прикосновении к элементу управления (*), поэтому вы должны сказать, что элемент управления касается, когда что-то происходит. Не пропустите, ваш ввод в пользовательский элемент управления формы не трогал, но не сам пользовательский контроль формы.

Вы можете использовать (размытие)

<div [formGroup]="parts">
  <input matInput mask="0000-00-00 00:00:00" 
      formControlName="datetime" 
      (blur)="onTouched()"
      (input)="_handleInput()" />
</div>

Обновление Я вижучто вы применили mat-error к FormControl "fromDate". Таким образом, валидатор должен быть применен к formControl, а не к FormGroup - иначе, это formGroup, которая недействительна -

Пользовательские валидаторы должны быть тогда

fromToRangeValidator(): ValidatorFn {
      //see that the argument is a FormControl
      return (control: FormControl): ValidationErrors => {
        //the formGroup is control.parent
        const group=control.parent;
        //but we must sure that is defined
        if (!group) return null;
        const fromDate = group.get('fromDatetime');
        const toDate = group.get('toDatetime');
        //...rest of your code...
        if (fromDate.value !== '' && toDate.value !== '') {
          const fromMoment = moment(fromDate.value, 'YYYYMMDDHHmmss');
          const toMoment = moment(toDate.value, 'YYYYMMDDHHmmss');
          if (toMoment.isBefore(fromMoment)) {
            fromDate.setErrors({greaterThen: true});
            toDate.setErrors({lessThen: true});
          }
        }
        return;
      };
    }
}

И, когда вы создаетеЕсли вы применили валидатор к элементу управления

form=new FormGroup({
    fromDatetime:new FormControl('',this.fromToRangeValidator()),
    toDatetime:new FormControl()
  })

(*) Действительно, вы можете изменить это поведение с помощью пользовательского ErrorStateMatcher, но это не запланированный вопрос

...