Browse Source

Task/migrate historical market data editor dialog from ngModel to form control

Replace the two ngModel bindings of the historical market data editor
dialog with a reactive FormGroup (date: disabled control, marketPrice:
required control), following the pattern of the prompt dialog and the
rule settings dialog (#7037).

The Save button is now a submit button that is disabled while the form is
pristine or invalid; Cancel and "Fetch market price" are explicit
type="button" so they do not submit the form. Fetching a market price sets
the control value and marks it dirty, so the fetched value can be saved.

Closes #6488

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pull/7933/head
yar01110 4 weeks ago
parent
commit
0c679fc7ba
  1. 1
      CHANGELOG.md
  2. 24
      libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts
  3. 24
      libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html

1
CHANGELOG.md

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Migrated the historical market data editor dialog from `ngModel` to form control
- Upgraded `bull-board` from version `9.9.0` to `9.10.1` - Upgraded `bull-board` from version `9.9.0` to `9.10.1`
## 3.72.0 - 2026-09-20 ## 3.72.0 - 2026-09-20

24
libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts

@ -7,11 +7,15 @@ import {
CUSTOM_ELEMENTS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA,
DestroyRef, DestroyRef,
OnInit, OnInit,
inject, inject
signal
} from '@angular/core'; } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import {
FormControl,
FormGroup,
ReactiveFormsModule,
Validators
} from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core'; import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
@ -33,7 +37,6 @@ import { HistoricalMarketDataEditorDialogParams } from './interfaces/interfaces'
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'h-100' }, host: { class: 'h-100' },
imports: [ imports: [
FormsModule,
IonIcon, IonIcon,
MatButtonModule, MatButtonModule,
MatDatepickerModule, MatDatepickerModule,
@ -51,7 +54,10 @@ export class GfHistoricalMarketDataEditorDialogComponent implements OnInit {
public readonly data = public readonly data =
inject<HistoricalMarketDataEditorDialogParams>(MAT_DIALOG_DATA); inject<HistoricalMarketDataEditorDialogParams>(MAT_DIALOG_DATA);
protected readonly marketPrice = signal(this.data.marketPrice); protected readonly historicalMarketDataForm = new FormGroup({
date: new FormControl({ disabled: true, value: this.data.dateString }),
marketPrice: new FormControl(this.data.marketPrice, Validators.required)
});
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
private readonly locale = private readonly locale =
@ -84,14 +90,18 @@ export class GfHistoricalMarketDataEditorDialogComponent implements OnInit {
}) })
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(({ marketPrice }) => { .subscribe(({ marketPrice }) => {
this.marketPrice.set(marketPrice); this.historicalMarketDataForm.controls.marketPrice.setValue(
marketPrice
);
this.historicalMarketDataForm.controls.marketPrice.markAsDirty();
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }
public onUpdate() { public onUpdate() {
const marketPrice = this.marketPrice(); const marketPrice =
this.historicalMarketDataForm.controls.marketPrice.value;
if (isNil(marketPrice)) { if (isNil(marketPrice)) {
return; return;

24
libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html

@ -1,15 +1,18 @@
<form class="d-flex flex-column h-100"> <form
class="d-flex flex-column h-100"
[formGroup]="historicalMarketDataForm"
(ngSubmit)="onUpdate()"
>
<h1 i18n mat-dialog-title>Details for {{ data.symbol }}</h1> <h1 i18n mat-dialog-title>Details for {{ data.symbol }}</h1>
<div class="flex-grow-1 py-3" mat-dialog-content> <div class="flex-grow-1 py-3" mat-dialog-content>
<div class="mb-3"> <div class="mb-3">
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Date</mat-label> <mat-label i18n>Date</mat-label>
<input <input
disabled formControlName="date"
matInput matInput
name="date" name="date"
[matDatepicker]="date" [matDatepicker]="date"
[(ngModel)]="data.dateString"
/> />
<mat-datepicker-toggle class="mr-2" matSuffix [for]="date"> <mat-datepicker-toggle class="mr-2" matSuffix [for]="date">
<ion-icon <ion-icon
@ -25,11 +28,10 @@
<mat-form-field appearance="outline" class="w-100"> <mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Market Price</mat-label> <mat-label i18n>Market Price</mat-label>
<input <input
formControlName="marketPrice"
matInput matInput
name="marketPrice" name="marketPrice"
type="number" type="number"
[ngModel]="marketPrice()"
(ngModelChange)="marketPrice.set($event)"
/> />
<span class="ml-2" matTextSuffix>{{ data.currency }}</span> <span class="ml-2" matTextSuffix>{{ data.currency }}</span>
</mat-form-field> </mat-form-field>
@ -38,6 +40,7 @@
i18n-title i18n-title
mat-button mat-button
title="Fetch market price" title="Fetch market price"
type="button"
(click)="onFetchSymbolForDate()" (click)="onFetchSymbolForDate()"
> >
<ion-icon class="text-muted" name="refresh-outline" /> <ion-icon class="text-muted" name="refresh-outline" />
@ -45,8 +48,15 @@
</div> </div>
</div> </div>
<div class="justify-content-end" mat-dialog-actions> <div class="justify-content-end" mat-dialog-actions>
<button i18n mat-button (click)="onCancel()">Cancel</button> <button i18n mat-button type="button" (click)="onCancel()">Cancel</button>
<button color="primary" mat-flat-button (click)="onUpdate()"> <button
color="primary"
mat-flat-button
type="submit"
[disabled]="
!(historicalMarketDataForm.dirty && historicalMarketDataForm.valid)
"
>
<ng-container i18n>Save</ng-container> <ng-container i18n>Save</ng-container>
</button> </button>
</div> </div>

Loading…
Cancel
Save