Browse Source

Task/improve type safety in portfolio filter form component (#6404)

* fix(lib): resolve typescript errors in portfolio filter form

* fix(lib): type safety in template

* feat(lib): implement takeUntilDestroyed

* feat(lib): replace constructor params with injections

* feat(lib): change accounts to input signal

* feat(lib): change assetClasses to input signal

* feat(lib): change holdings to input signal

* feat(lib): change tags to input signal

* fix(lib): implement signal for disabled

* fix(lib): implement model signal for disabled

* fix(lib): reduce any types
pull/6409/head
Kenrick Tandrian 1 day ago
committed by GitHub
parent
commit
9493f79f8e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html
  2. 4
      libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.stories.ts
  3. 69
      libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts

12
libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.html

@ -4,14 +4,14 @@
<mat-label i18n>Account</mat-label>
<mat-select formControlName="account">
<mat-option [value]="null" />
@for (account of accounts; track account.id) {
@for (account of accounts(); track account.id) {
<mat-option [value]="account.id">
<div class="d-flex">
@if (account.platform?.url) {
<gf-entity-logo
class="mr-1"
[tooltip]="account.platform?.name"
[url]="account.platform?.url"
[tooltip]="account.platform?.name ?? ''"
[url]="account.platform?.url ?? ''"
/>
}
<span>{{ account.name }}</span>
@ -32,7 +32,7 @@
filterForm.get('holding')?.value?.name
}}</mat-select-trigger>
<mat-option [value]="null" />
@for (holding of holdings; track holding.name) {
@for (holding of holdings(); track holding.name) {
<mat-option [value]="holding">
<div class="line-height-1 text-truncate">
<span
@ -53,7 +53,7 @@
<mat-label i18n>Tag</mat-label>
<mat-select formControlName="tag">
<mat-option [value]="null" />
@for (tag of tags; track tag.id) {
@for (tag of tags(); track tag.id) {
<mat-option [value]="tag.id">{{ tag.label }}</mat-option>
}
</mat-select>
@ -64,7 +64,7 @@
<mat-label i18n>Asset Class</mat-label>
<mat-select formControlName="assetClass">
<mat-option [value]="null" />
@for (assetClass of assetClasses; track assetClass.id) {
@for (assetClass of assetClasses(); track assetClass.id) {
<mat-option [value]="assetClass.id">{{
assetClass.label
}}</mat-option>

4
libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.stories.ts

@ -40,7 +40,7 @@ export const Default: Story = {
{ id: 'COMMODITY', label: 'Commodity', type: 'ASSET_CLASS' },
{ id: 'EQUITY', label: 'Equity', type: 'ASSET_CLASS' },
{ id: 'FIXED_INCOME', label: 'Fixed Income', type: 'ASSET_CLASS' }
] as any,
],
holdings: [
{
currency: 'USD',
@ -66,7 +66,7 @@ export const Default: Story = {
label: 'Retirement Fund',
type: 'TAG'
}
] as any,
],
disabled: false
}
};

69
libs/ui/src/lib/portfolio-filter-form/portfolio-filter-form.component.ts

@ -8,12 +8,15 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Input,
DestroyRef,
OnChanges,
OnDestroy,
OnInit,
forwardRef
forwardRef,
inject,
input,
model
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import {
ControlValueAccessor,
FormBuilder,
@ -25,7 +28,6 @@ import {
} from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { Subject, takeUntil } from 'rxjs';
import { GfEntityLogoComponent } from '../entity-logo/entity-logo.component';
import { PortfolioFilterFormValue } from './interfaces';
@ -53,33 +55,37 @@ import { PortfolioFilterFormValue } from './interfaces';
templateUrl: './portfolio-filter-form.component.html'
})
export class GfPortfolioFilterFormComponent
implements ControlValueAccessor, OnInit, OnChanges, OnDestroy
implements ControlValueAccessor, OnChanges, OnInit
{
@Input() accounts: AccountWithPlatform[] = [];
@Input() assetClasses: Filter[] = [];
@Input() holdings: PortfolioPosition[] = [];
@Input() tags: Filter[] = [];
@Input() disabled = false;
public filterForm: FormGroup;
private unsubscribeSubject = new Subject<void>();
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private formBuilder: FormBuilder
) {
public readonly accounts = input<AccountWithPlatform[]>([]);
public readonly assetClasses = input<Filter[]>([]);
public readonly disabled = model(false);
public readonly holdings = input<PortfolioPosition[]>([]);
public readonly tags = input<Filter[]>([]);
public filterForm: FormGroup<{
account: FormControl<string | null>;
assetClass: FormControl<string | null>;
holding: FormControl<PortfolioPosition | null>;
tag: FormControl<string | null>;
}>;
private readonly changeDetectorRef = inject(ChangeDetectorRef);
private readonly destroyRef = inject(DestroyRef);
private readonly formBuilder = inject(FormBuilder);
public constructor() {
this.filterForm = this.formBuilder.group({
account: new FormControl<string>(null),
assetClass: new FormControl<string>(null),
holding: new FormControl<PortfolioPosition>(null),
tag: new FormControl<string>(null)
account: new FormControl<string | null>(null),
assetClass: new FormControl<string | null>(null),
holding: new FormControl<PortfolioPosition | null>(null),
tag: new FormControl<string | null>(null)
});
}
public ngOnInit() {
this.filterForm.valueChanges
.pipe(takeUntil(this.unsubscribeSubject))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
this.onChange(value as PortfolioFilterFormValue);
this.onTouched();
@ -108,7 +114,7 @@ export class GfPortfolioFilterFormComponent
}
public ngOnChanges() {
if (this.disabled) {
if (this.disabled()) {
this.filterForm.disable({ emitEvent: false });
} else {
this.filterForm.enable({ emitEvent: false });
@ -116,9 +122,9 @@ export class GfPortfolioFilterFormComponent
const tagControl = this.filterForm.get('tag');
if (this.tags.length === 0) {
if (this.tags().length === 0) {
tagControl?.disable({ emitEvent: false });
} else if (!this.disabled) {
} else if (!this.disabled()) {
tagControl?.enable({ emitEvent: false });
}
@ -134,9 +140,9 @@ export class GfPortfolioFilterFormComponent
}
public setDisabledState(isDisabled: boolean) {
this.disabled = isDisabled;
this.disabled.set(isDisabled);
if (this.disabled) {
if (this.disabled()) {
this.filterForm.disable({ emitEvent: false });
} else {
this.filterForm.enable({ emitEvent: false });
@ -161,11 +167,6 @@ export class GfPortfolioFilterFormComponent
}
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private onChange = (_value: PortfolioFilterFormValue): void => {
// ControlValueAccessor onChange callback

Loading…
Cancel
Save