You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

182 lines
3.8 KiB

import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {
Component,
DoCheck,
ElementRef,
HostBinding,
HostListener,
Input,
OnDestroy
} from '@angular/core';
import { ControlValueAccessor, NgControl, Validators } from '@angular/forms';
import { MatFormFieldControl } from '@angular/material/form-field';
import { Subject } from 'rxjs';
@Component({
template: '',
standalone: false
})
export abstract class AbstractMatFormField<T>
implements ControlValueAccessor, DoCheck, MatFormFieldControl<T>, OnDestroy
{
@HostBinding()
public id = `${this.controlType}-${AbstractMatFormField.nextId++}`;
@HostBinding('attr.aria-describedBy') public describedBy = '';
public readonly autofilled: boolean;
public errorState: boolean;
public focused = false;
public readonly stateChanges = new Subject<void>();
public readonly userAriaDescribedBy: string;
protected onChange?: (value: T) => void;
protected onTouched?: () => void;
private static nextId = 0;
protected constructor(
protected _elementRef: ElementRef,
protected _focusMonitor: FocusMonitor,
public readonly ngControl: NgControl
) {
if (this.ngControl) {
this.ngControl.valueAccessor = this;
}
_focusMonitor
.monitor(this._elementRef.nativeElement, true)
.subscribe((origin) => {
this.focused = !!origin;
this.stateChanges.next();
});
}
private _controlType: string;
public get controlType(): string {
return this._controlType;
}
protected set controlType(value: string) {
this._controlType = value;
this.id = `${this._controlType}-${AbstractMatFormField.nextId++}`;
}
private _value: T;
public get value(): T {
return this._value;
}
public set value(value: T) {
this._value = value;
if (this.onChange) {
this.onChange(value);
}
}
public get empty(): boolean {
return !this._value;
}
public _placeholder = '';
public get placeholder() {
return this._placeholder;
}
@Input()
public set placeholder(placeholder: string) {
this._placeholder = placeholder;
this.stateChanges.next();
}
public _required = false;
public get required() {
return (
this._required ||
this.ngControl.control?.hasValidator(Validators.required)
);
}
@Input()
public set required(required: any) {
this._required = coerceBooleanProperty(required);
this.stateChanges.next();
}
public _disabled = false;
public get disabled() {
if (this.ngControl?.disabled !== null) {
return this.ngControl.disabled;
}
return this._disabled;
}
@Input()
public set disabled(disabled: any) {
this._disabled = coerceBooleanProperty(disabled);
if (this.focused) {
this.focused = false;
this.stateChanges.next();
}
}
public abstract focus(): void;
public get shouldLabelFloat(): boolean {
return this.focused || !this.empty;
}
public ngDoCheck() {
if (this.ngControl) {
this.errorState = this.ngControl.invalid && this.ngControl.touched;
this.stateChanges.next();
}
}
public ngOnDestroy() {
this.stateChanges.complete();
this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
}
public registerOnChange(fn: (_: T) => void) {
this.onChange = fn;
}
public registerOnTouched(fn: () => void) {
this.onTouched = fn;
}
public setDescribedByIds(ids: string[]) {
this.describedBy = ids.join(' ');
}
public writeValue(value: T) {
this.value = value;
}
@HostListener('focusout')
public onBlur() {
this.focused = false;
if (this.onTouched) {
this.onTouched();
}
this.stateChanges.next();
}
public onContainerClick() {
if (!this.focused) {
this.focus();
}
}
}