feat: added user search to various components
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { Component, EventEmitter, forwardRef, Input, Output, ViewChild } from '@angular/core';
|
||||
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { Component, DoCheck, ElementRef, HostBinding, Input, OnDestroy, Optional, Self } from '@angular/core';
|
||||
import { ControlValueAccessor, FormControl, FormGroupDirective, NgControl, NgForm } from '@angular/forms';
|
||||
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
||||
import { MatInput } from '@angular/material/input';
|
||||
import { MatFormFieldControl } from '@angular/material/form-field';
|
||||
import { Subject } from 'rxjs';
|
||||
import { AdminCommService } from 'src/app/admin-view/admin-comm.service';
|
||||
|
||||
interface UserSearchResult {
|
||||
export interface UserSearchResult {
|
||||
_id: string;
|
||||
fname: string;
|
||||
surname: string;
|
||||
@@ -18,26 +20,120 @@ interface UserSearchResult {
|
||||
styleUrl: './user-search.component.scss',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => UserSearchComponent),
|
||||
multi: true
|
||||
provide: MatFormFieldControl,
|
||||
useExisting: UserSearchComponent
|
||||
}
|
||||
],
|
||||
host: {
|
||||
'(blur)': '_onTouched()'
|
||||
}
|
||||
})
|
||||
export class UserSearchComponent implements ControlValueAccessor {
|
||||
export class UserSearchComponent implements ControlValueAccessor, MatFormFieldControl<UserSearchResult>, OnDestroy, DoCheck {
|
||||
protected loading: boolean = false
|
||||
@Input() label?: boolean
|
||||
control: FormControl = new FormControl();
|
||||
protected list: UserSearchResult[] = []
|
||||
private timeout?: NodeJS.Timeout
|
||||
private _onChange!: (_: UserSearchResult) => void
|
||||
private _onTouched!: any
|
||||
|
||||
constructor(readonly acu: AdminCommService) {
|
||||
static nextId = 0;
|
||||
|
||||
@Input()
|
||||
public get value(): UserSearchResult | null {
|
||||
return this.control.value;
|
||||
}
|
||||
|
||||
public set value(value: UserSearchResult | null) {
|
||||
this.control.setValue(value)
|
||||
this.stateChanges.next()
|
||||
}
|
||||
|
||||
touched = false
|
||||
|
||||
stateChanges = new Subject<void>();
|
||||
|
||||
@HostBinding() id: string = `app-user-search-${UserSearchComponent.nextId++}`;
|
||||
|
||||
private _placeholder: string = "";
|
||||
@Input()
|
||||
public get placeholder(): string {
|
||||
return this._placeholder;
|
||||
}
|
||||
public set placeholder(value: string) {
|
||||
this._placeholder = value;
|
||||
this.stateChanges.next()
|
||||
}
|
||||
|
||||
focused: boolean = false;
|
||||
onFocusIn(event: FocusEvent) {
|
||||
if (!this.focused) {
|
||||
this.focused = true;
|
||||
this.stateChanges.next();
|
||||
}
|
||||
}
|
||||
|
||||
onFocusOut(event: FocusEvent) {
|
||||
if (!this._elementRef.nativeElement.contains(event.relatedTarget as Element)) {
|
||||
this.touched = true
|
||||
this.focused = false;
|
||||
this._onTouched();
|
||||
this.stateChanges.next();
|
||||
}
|
||||
}
|
||||
get empty(): boolean {
|
||||
return !this.control.value
|
||||
}
|
||||
@HostBinding('class.floating')
|
||||
get shouldLabelFloat(): boolean {
|
||||
return this.focused || !this.empty
|
||||
}
|
||||
private _required: boolean = false;
|
||||
@Input()
|
||||
public get required(): boolean {
|
||||
return this._required;
|
||||
}
|
||||
|
||||
public set required(value: BooleanInput) {
|
||||
this._required = coerceBooleanProperty(value);
|
||||
this.stateChanges.next()
|
||||
}
|
||||
|
||||
private _disabled: boolean = false;
|
||||
@Input()
|
||||
public get disabled(): boolean {
|
||||
return this._disabled;
|
||||
}
|
||||
public set disabled(value: BooleanInput) {
|
||||
this._disabled = coerceBooleanProperty(value);
|
||||
this._disabled ? this.control.disable() : this.control.enable()
|
||||
this.stateChanges.next()
|
||||
}
|
||||
errorState: boolean = false
|
||||
controlType?: string | undefined = "app-user-search";
|
||||
autofilled?: boolean | undefined;
|
||||
@Input('aria-describedby') userAriaDescribedBy?: string;
|
||||
setDescribedByIds(ids: string[]): void {
|
||||
const controlElement = this._elementRef.nativeElement.querySelector('.app-user-search-container')!;
|
||||
controlElement.setAttribute('aria-describedby', ids.join(' '))
|
||||
}
|
||||
onContainerClick(event: MouseEvent): void {
|
||||
if ((event.target as Element).tagName.toLowerCase() != 'input') {
|
||||
this._elementRef.nativeElement.querySelector('input').focus()
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly acu: AdminCommService,
|
||||
@Optional() @Self() public ngControl: NgControl,
|
||||
@Optional() private _parentForm: NgForm,
|
||||
@Optional() private _parentFormGroup: FormGroupDirective,
|
||||
private _elementRef: ElementRef
|
||||
) {
|
||||
if (this.ngControl != null) {
|
||||
(this.ngControl as NgControl).valueAccessor = this
|
||||
}
|
||||
this.control.valueChanges.subscribe(() => {
|
||||
if (typeof this.control.value == "object") return;
|
||||
this.loading = true
|
||||
if (this.timeout) clearTimeout(this.timeout)
|
||||
this.timeout = setTimeout(() => {
|
||||
@@ -48,9 +144,28 @@ export class UserSearchComponent implements ControlValueAccessor {
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
ngDoCheck(): void {
|
||||
if (this.ngControl) {
|
||||
this.updateErrorState()
|
||||
}
|
||||
}
|
||||
private updateErrorState() {
|
||||
const parent = this._parentFormGroup || this._parentForm
|
||||
|
||||
writeValue(obj: string): void {
|
||||
this.control.setValue(obj)
|
||||
const oldState = this.errorState;
|
||||
const newState = (this.ngControl?.invalid || this.control.invalid) && (this.touched || parent.submitted);
|
||||
|
||||
if (oldState !== newState) {
|
||||
this.errorState = newState
|
||||
this.stateChanges.next()
|
||||
}
|
||||
}
|
||||
ngOnDestroy(): void {
|
||||
this.stateChanges.complete()
|
||||
}
|
||||
|
||||
writeValue(obj: UserSearchResult): void {
|
||||
this.value = obj
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: UserSearchResult) => void): void {
|
||||
@@ -62,7 +177,7 @@ export class UserSearchComponent implements ControlValueAccessor {
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
isDisabled ? this.control.disable() : this.control.enable()
|
||||
this.disabled = isDisabled
|
||||
}
|
||||
|
||||
protected displayFn(u: UserSearchResult): string {
|
||||
@@ -71,6 +186,8 @@ export class UserSearchComponent implements ControlValueAccessor {
|
||||
}
|
||||
|
||||
protected saveValue(e: MatAutocompleteSelectedEvent) {
|
||||
this._onChange(this.control.value)
|
||||
this.autofilled = true
|
||||
this.value = e.option.value
|
||||
this._onChange(this.value!)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user