Browse Source

fix(rule-settings-dialog): disable Save button until form is modified

The Save button was always enabled on open, even before a user interacted
with the threshold sliders. Migrated from ngModel/FormsModule to
FormGroup/ReactiveFormsModule so the form dirty state can be tracked.
The Save button is now disabled when the form is pristine (no changes made).
pull/6741/head
Ai-chan-0411 (藍) 7 days ago
parent
commit
06001424ce
Failed to extract signature
  1. 18
      apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts
  2. 265
      apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html

18
apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts

@ -3,7 +3,7 @@ import { GfValueComponent } from '@ghostfolio/ui/value';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { import {
MAT_DIALOG_DATA, MAT_DIALOG_DATA,
@ -17,21 +17,31 @@ import { RuleSettingsDialogParams } from './interfaces/interfaces';
@Component({ @Component({
imports: [ imports: [
CommonModule, CommonModule,
FormsModule,
GfValueComponent, GfValueComponent,
MatButtonModule, MatButtonModule,
MatDialogModule, MatDialogModule,
MatSliderModule MatSliderModule,
ReactiveFormsModule
], ],
selector: 'gf-rule-settings-dialog', selector: 'gf-rule-settings-dialog',
styleUrls: ['./rule-settings-dialog.scss'], styleUrls: ['./rule-settings-dialog.scss'],
templateUrl: './rule-settings-dialog.html' templateUrl: './rule-settings-dialog.html'
}) })
export class GfRuleSettingsDialogComponent { export class GfRuleSettingsDialogComponent {
public settings: XRayRulesSettings['AccountClusterRiskCurrentInvestment']; public settingsForm = new FormGroup({
thresholdMax: new FormControl(this.data.settings?.thresholdMax ?? null),
thresholdMin: new FormControl(this.data.settings?.thresholdMin ?? null)
});
public constructor( public constructor(
@Inject(MAT_DIALOG_DATA) public data: RuleSettingsDialogParams, @Inject(MAT_DIALOG_DATA) public data: RuleSettingsDialogParams,
public dialogRef: MatDialogRef<GfRuleSettingsDialogComponent> public dialogRef: MatDialogRef<GfRuleSettingsDialogComponent>
) {} ) {}
public onSave() {
this.dialogRef.close({
...this.data.settings,
...this.settingsForm.value
});
}
} }

265
apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html

@ -1,138 +1,139 @@
<div mat-dialog-title>{{ data.categoryName }} › {{ data.rule.name }}</div> <div mat-dialog-title>{{ data.categoryName }} › {{ data.rule.name }}</div>
<div class="py-3" mat-dialog-content> <form [formGroup]="settingsForm">
@if ( <div class="py-3" mat-dialog-content>
data.rule.configuration.thresholdMin && data.rule.configuration.thresholdMax @if (
) { data.rule.configuration.thresholdMin && data.rule.configuration.thresholdMax
<div class="w-100"> ) {
<h6 class="d-flex mb-0"> <div class="w-100">
<ng-container i18n>Threshold range</ng-container>: <h6 class="d-flex mb-0">
<gf-value <ng-container i18n>Threshold range</ng-container>:
class="ml-1" <gf-value
[isPercent]="data.rule.configuration.threshold.unit === '%'" class="ml-1"
[locale]="data.locale" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[precision]="2" [locale]="data.locale"
[value]="data.settings.thresholdMin" [precision]="2"
/> [value]="settingsForm.controls.thresholdMin.value"
<span class="mx-1">-</span> />
<gf-value <span class="mx-1">-</span>
[isPercent]="data.rule.configuration.threshold.unit === '%'" <gf-value
[locale]="data.locale" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[precision]="2" [locale]="data.locale"
[value]="data.settings.thresholdMax" [precision]="2"
/> [value]="settingsForm.controls.thresholdMax.value"
</h6> />
<div class="align-items-center d-flex w-100"> </h6>
<gf-value <div class="align-items-center d-flex w-100">
[isPercent]="data.rule.configuration.threshold.unit === '%'" <gf-value
[locale]="data.locale" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[precision]="2" [locale]="data.locale"
[value]="data.rule.configuration.threshold.min" [precision]="2"
/> [value]="data.rule.configuration.threshold.min"
<mat-slider />
class="flex-grow-1" <mat-slider
[max]="data.rule.configuration.threshold.max" class="flex-grow-1"
[min]="data.rule.configuration.threshold.min" [max]="data.rule.configuration.threshold.max"
[step]="data.rule.configuration.threshold.step" [min]="data.rule.configuration.threshold.min"
> [step]="data.rule.configuration.threshold.step"
<input matSliderStartThumb [(ngModel)]="data.settings.thresholdMin" /> >
<input matSliderEndThumb [(ngModel)]="data.settings.thresholdMax" /> <input formControlName="thresholdMin" matSliderStartThumb />
</mat-slider> <input formControlName="thresholdMax" matSliderEndThumb />
<gf-value </mat-slider>
[isPercent]="data.rule.configuration.threshold.unit === '%'" <gf-value
[locale]="data.locale" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[precision]="2" [locale]="data.locale"
[value]="data.rule.configuration.threshold.max" [precision]="2"
/> [value]="data.rule.configuration.threshold.max"
/>
</div>
</div> </div>
</div> } @else {
} @else { <div
<div class="w-100"
class="w-100" [ngClass]="{ 'd-none': !data.rule.configuration.thresholdMin }"
[ngClass]="{ 'd-none': !data.rule.configuration.thresholdMin }" >
> <h6 class="d-flex mb-0">
<h6 class="d-flex mb-0"> <ng-container i18n>Threshold Min</ng-container>:
<ng-container i18n>Threshold Min</ng-container>: <gf-value
<gf-value class="ml-1"
class="ml-1" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[isPercent]="data.rule.configuration.threshold.unit === '%'" [locale]="data.locale"
[locale]="data.locale" [precision]="2"
[precision]="2" [value]="settingsForm.controls.thresholdMin.value"
[value]="data.settings.thresholdMin" />
/> </h6>
</h6> <div class="align-items-center d-flex w-100">
<div class="align-items-center d-flex w-100"> <gf-value
<gf-value [isPercent]="data.rule.configuration.threshold.unit === '%'"
[isPercent]="data.rule.configuration.threshold.unit === '%'" [locale]="data.locale"
[locale]="data.locale" [precision]="2"
[precision]="2" [value]="data.rule.configuration.threshold.min"
[value]="data.rule.configuration.threshold.min" />
/> <mat-slider
<mat-slider class="flex-grow-1"
class="flex-grow-1" [max]="data.rule.configuration.threshold.max"
name="thresholdMin" [min]="data.rule.configuration.threshold.min"
[max]="data.rule.configuration.threshold.max" [step]="data.rule.configuration.threshold.step"
[min]="data.rule.configuration.threshold.min" >
[step]="data.rule.configuration.threshold.step" <input formControlName="thresholdMin" matSliderThumb />
> </mat-slider>
<input matSliderThumb [(ngModel)]="data.settings.thresholdMin" /> <gf-value
</mat-slider> [isPercent]="data.rule.configuration.threshold.unit === '%'"
<gf-value [locale]="data.locale"
[isPercent]="data.rule.configuration.threshold.unit === '%'" [precision]="2"
[locale]="data.locale" [value]="data.rule.configuration.threshold.max"
[precision]="2" />
[value]="data.rule.configuration.threshold.max" </div>
/>
</div> </div>
</div> <div
<div class="w-100"
class="w-100" [ngClass]="{ 'd-none': !data.rule.configuration.thresholdMax }"
[ngClass]="{ 'd-none': !data.rule.configuration.thresholdMax }" >
> <h6 class="d-flex mb-0">
<h6 class="d-flex mb-0"> <ng-container i18n>Threshold Max</ng-container>:
<ng-container i18n>Threshold Max</ng-container>: <gf-value
<gf-value class="ml-1"
class="ml-1" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[isPercent]="data.rule.configuration.threshold.unit === '%'" [locale]="data.locale"
[locale]="data.locale" [precision]="2"
[precision]="2" [value]="settingsForm.controls.thresholdMax.value"
[value]="data.settings.thresholdMax" />
/> </h6>
</h6> <div class="align-items-center d-flex w-100">
<div class="align-items-center d-flex w-100"> <gf-value
<gf-value [isPercent]="data.rule.configuration.threshold.unit === '%'"
[isPercent]="data.rule.configuration.threshold.unit === '%'" [locale]="data.locale"
[locale]="data.locale" [precision]="2"
[precision]="2" [value]="data.rule.configuration.threshold.min"
[value]="data.rule.configuration.threshold.min" />
/> <mat-slider
<mat-slider class="flex-grow-1"
class="flex-grow-1" [max]="data.rule.configuration.threshold.max"
name="thresholdMax" [min]="data.rule.configuration.threshold.min"
[max]="data.rule.configuration.threshold.max" [step]="data.rule.configuration.threshold.step"
[min]="data.rule.configuration.threshold.min" >
[step]="data.rule.configuration.threshold.step" <input formControlName="thresholdMax" matSliderThumb />
> </mat-slider>
<input matSliderThumb [(ngModel)]="data.settings.thresholdMax" /> <gf-value
</mat-slider> [isPercent]="data.rule.configuration.threshold.unit === '%'"
<gf-value [locale]="data.locale"
[isPercent]="data.rule.configuration.threshold.unit === '%'" [precision]="2"
[locale]="data.locale" [value]="data.rule.configuration.threshold.max"
[precision]="2" />
[value]="data.rule.configuration.threshold.max" </div>
/>
</div> </div>
</div> }
} </div>
</div>
<div align="end" mat-dialog-actions> <div align="end" mat-dialog-actions>
<button i18n mat-button (click)="dialogRef.close()">Close</button> <button i18n mat-button (click)="dialogRef.close()">Close</button>
<button <button
color="primary" color="primary"
mat-flat-button mat-flat-button
(click)="dialogRef.close(data.settings)" [disabled]="settingsForm.pristine"
> (click)="onSave()"
<ng-container i18n>Save</ng-container> >
</button> <ng-container i18n>Save</ng-container>
</div> </button>
</div>
</form>

Loading…
Cancel
Save