Create reusable currency selector component using mat-autocomplete (#2487)
* Create reusable currency selector component using mat-autocomplete * Update changelogpull/2534/head^2
parent
c4e8e37884
commit
96b5dcfaf8
@ -0,0 +1,4 @@
|
||||
export interface Currency {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<input
|
||||
autocapitalize="off"
|
||||
autocomplete="off"
|
||||
matInput
|
||||
[formControl]="control"
|
||||
[matAutocomplete]="currencyAutocomplete"
|
||||
/>
|
||||
|
||||
<mat-autocomplete
|
||||
#currencyAutocomplete="matAutocomplete"
|
||||
[displayWith]="displayFn"
|
||||
(optionSelected)="onUpdateCurrency($event)"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let currencyItem of filteredCurrencies"
|
||||
class="line-height-1"
|
||||
[value]="currencyItem"
|
||||
>
|
||||
{{ currencyItem.label }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
@ -0,0 +1,3 @@
|
||||
:host {
|
||||
display: block;
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
import { FocusMonitor } from '@angular/cdk/a11y';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { FormControl, FormGroupDirective, NgControl } from '@angular/forms';
|
||||
import {
|
||||
MatAutocomplete,
|
||||
MatAutocompleteSelectedEvent
|
||||
} from '@angular/material/autocomplete';
|
||||
import { MatFormFieldControl } from '@angular/material/form-field';
|
||||
import { MatInput } from '@angular/material/input';
|
||||
import { Currency } from '@ghostfolio/common/interfaces/currency.interface';
|
||||
import { AbstractMatFormField } from '@ghostfolio/ui/shared/abstract-mat-form-field';
|
||||
import { Subject } from 'rxjs';
|
||||
import { map, startWith, takeUntil } from 'rxjs/operators';
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: {
|
||||
'[attr.aria-describedBy]': 'describedBy',
|
||||
'[id]': 'id'
|
||||
},
|
||||
providers: [
|
||||
{
|
||||
provide: MatFormFieldControl,
|
||||
useExisting: CurrencySelectorComponent
|
||||
}
|
||||
],
|
||||
selector: 'gf-currency-selector',
|
||||
styleUrls: ['./currency-selector.component.scss'],
|
||||
templateUrl: 'currency-selector.component.html'
|
||||
})
|
||||
export class CurrencySelectorComponent
|
||||
extends AbstractMatFormField<Currency>
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
@Input() private currencies: Currency[] = [];
|
||||
@Input() private formControlName: string;
|
||||
|
||||
@ViewChild(MatInput) private input: MatInput;
|
||||
|
||||
@ViewChild('currencyAutocomplete')
|
||||
public currencyAutocomplete: MatAutocomplete;
|
||||
|
||||
public control = new FormControl();
|
||||
public filteredCurrencies: Currency[] = [];
|
||||
|
||||
private unsubscribeSubject = new Subject<void>();
|
||||
|
||||
public constructor(
|
||||
public readonly _elementRef: ElementRef,
|
||||
public readonly _focusMonitor: FocusMonitor,
|
||||
public readonly changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly formGroupDirective: FormGroupDirective,
|
||||
public readonly ngControl: NgControl
|
||||
) {
|
||||
super(_elementRef, _focusMonitor, ngControl);
|
||||
|
||||
this.controlType = 'currency-selector';
|
||||
}
|
||||
|
||||
public ngOnInit() {
|
||||
if (this.disabled) {
|
||||
this.control.disable();
|
||||
}
|
||||
|
||||
const formGroup = this.formGroupDirective.form;
|
||||
|
||||
if (formGroup) {
|
||||
const control = formGroup.get(this.formControlName);
|
||||
|
||||
if (control) {
|
||||
this.value = this.currencies.find(({ value }) => {
|
||||
return value === control.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.control.valueChanges
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
if (super.value?.value) {
|
||||
super.value.value = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.control.valueChanges
|
||||
.pipe(
|
||||
takeUntil(this.unsubscribeSubject),
|
||||
startWith(''),
|
||||
map((value) => {
|
||||
return value ? this.filter(value) : this.currencies.slice();
|
||||
})
|
||||
)
|
||||
.subscribe((values) => {
|
||||
this.filteredCurrencies = values;
|
||||
});
|
||||
}
|
||||
|
||||
public displayFn(currency: Currency) {
|
||||
return currency?.label ?? '';
|
||||
}
|
||||
|
||||
public get empty() {
|
||||
return this.input?.empty;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
public ngDoCheck() {
|
||||
if (this.ngControl) {
|
||||
this.validateRequired();
|
||||
this.errorState = this.ngControl.invalid && this.ngControl.touched;
|
||||
this.stateChanges.next();
|
||||
}
|
||||
}
|
||||
|
||||
public onUpdateCurrency(event: MatAutocompleteSelectedEvent) {
|
||||
super.value = {
|
||||
label: event.option.value.label,
|
||||
value: event.option.value.value
|
||||
} as Currency;
|
||||
}
|
||||
|
||||
public set value(value: Currency) {
|
||||
const newValue =
|
||||
typeof value === 'object' && value !== null ? { ...value } : value;
|
||||
this.control.setValue(newValue);
|
||||
super.value = newValue;
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
|
||||
this.unsubscribeSubject.next();
|
||||
this.unsubscribeSubject.complete();
|
||||
}
|
||||
|
||||
private filter(value: Currency | string) {
|
||||
const filterValue =
|
||||
typeof value === 'string'
|
||||
? value?.toLowerCase()
|
||||
: value?.value.toLowerCase();
|
||||
|
||||
return this.currencies.filter((currency) => {
|
||||
return currency.value.toLowerCase().startsWith(filterValue);
|
||||
});
|
||||
}
|
||||
|
||||
private validateRequired() {
|
||||
const requiredCheck = super.required
|
||||
? !super.value.label || !super.value.value
|
||||
: false;
|
||||
|
||||
if (requiredCheck) {
|
||||
this.ngControl.control.setErrors({ invalidData: true });
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
import { CurrencySelectorComponent } from './currency-selector.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [CurrencySelectorComponent],
|
||||
exports: [CurrencySelectorComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
MatAutocompleteModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class GfCurrencySelectorModule {}
|
Loading…
Reference in new issue