Я использую 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;
};
}
и форма не отправляется из-за ошибки, но ошибка по-прежнему не отображается