Browse Source

fix: disable Save button in rule settings dialog until form is dirty

Migrate from ngModel to FormControl so the Save button is disabled
on initialization and only enabled once the user interacts with
the slider controls.

Closes #6479
pull/6638/head
Bortlesboat 3 days ago
parent
commit
02e3e4e30f
No known key found for this signature in database GPG Key ID: A2B96F4BB60D03A1
  1. 4
      CHANGELOG.md
  2. 36
      apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.component.ts
  3. 19
      apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html

4
CHANGELOG.md

@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the language localization for Spanish (`es`) - Improved the language localization for Spanish (`es`)
- Upgraded `countries-list` from version `3.2.2` to `3.3.0` - Upgraded `countries-list` from version `3.2.2` to `3.3.0`
### Fixed
- Fixed the _Save_ button state in the rule settings dialog of the _X-ray_ section
## 2.251.0 - 2026-03-24 ## 2.251.0 - 2026-03-24
### Added ### Added

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

@ -2,8 +2,8 @@ import { XRayRulesSettings } from '@ghostfolio/common/interfaces';
import { GfValueComponent } from '@ghostfolio/ui/value'; 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, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormControl, 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,47 @@ 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 implements OnInit {
public settings: XRayRulesSettings['AccountClusterRiskCurrentInvestment']; public settings: XRayRulesSettings['AccountClusterRiskCurrentInvestment'];
public thresholdMaxControl: FormControl<number>;
public thresholdMinControl: FormControl<number>;
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 get isDirty(): boolean {
return this.thresholdMinControl.dirty || this.thresholdMaxControl.dirty;
}
public ngOnInit() {
this.thresholdMinControl = new FormControl<number>(
this.data.settings.thresholdMin,
{ nonNullable: true }
);
this.thresholdMaxControl = new FormControl<number>(
this.data.settings.thresholdMax,
{ nonNullable: true }
);
}
public onSave() {
this.dialogRef.close({
...this.data.settings,
thresholdMax: this.thresholdMaxControl.value,
thresholdMin: this.thresholdMinControl.value
});
}
} }

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

@ -12,14 +12,14 @@
[isPercent]="data.rule.configuration.threshold.unit === '%'" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[locale]="data.locale" [locale]="data.locale"
[precision]="2" [precision]="2"
[value]="data.settings.thresholdMin" [value]="thresholdMinControl.value"
/> />
<span class="mx-1">-</span> <span class="mx-1">-</span>
<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.settings.thresholdMax" [value]="thresholdMaxControl.value"
/> />
</h6> </h6>
<div class="align-items-center d-flex w-100"> <div class="align-items-center d-flex w-100">
@ -35,8 +35,8 @@
[min]="data.rule.configuration.threshold.min" [min]="data.rule.configuration.threshold.min"
[step]="data.rule.configuration.threshold.step" [step]="data.rule.configuration.threshold.step"
> >
<input matSliderStartThumb [(ngModel)]="data.settings.thresholdMin" /> <input matSliderStartThumb [formControl]="thresholdMinControl" />
<input matSliderEndThumb [(ngModel)]="data.settings.thresholdMax" /> <input matSliderEndThumb [formControl]="thresholdMaxControl" />
</mat-slider> </mat-slider>
<gf-value <gf-value
[isPercent]="data.rule.configuration.threshold.unit === '%'" [isPercent]="data.rule.configuration.threshold.unit === '%'"
@ -58,7 +58,7 @@
[isPercent]="data.rule.configuration.threshold.unit === '%'" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[locale]="data.locale" [locale]="data.locale"
[precision]="2" [precision]="2"
[value]="data.settings.thresholdMin" [value]="thresholdMinControl.value"
/> />
</h6> </h6>
<div class="align-items-center d-flex w-100"> <div class="align-items-center d-flex w-100">
@ -75,7 +75,7 @@
[min]="data.rule.configuration.threshold.min" [min]="data.rule.configuration.threshold.min"
[step]="data.rule.configuration.threshold.step" [step]="data.rule.configuration.threshold.step"
> >
<input matSliderThumb [(ngModel)]="data.settings.thresholdMin" /> <input matSliderThumb [formControl]="thresholdMinControl" />
</mat-slider> </mat-slider>
<gf-value <gf-value
[isPercent]="data.rule.configuration.threshold.unit === '%'" [isPercent]="data.rule.configuration.threshold.unit === '%'"
@ -96,7 +96,7 @@
[isPercent]="data.rule.configuration.threshold.unit === '%'" [isPercent]="data.rule.configuration.threshold.unit === '%'"
[locale]="data.locale" [locale]="data.locale"
[precision]="2" [precision]="2"
[value]="data.settings.thresholdMax" [value]="thresholdMaxControl.value"
/> />
</h6> </h6>
<div class="align-items-center d-flex w-100"> <div class="align-items-center d-flex w-100">
@ -113,7 +113,7 @@
[min]="data.rule.configuration.threshold.min" [min]="data.rule.configuration.threshold.min"
[step]="data.rule.configuration.threshold.step" [step]="data.rule.configuration.threshold.step"
> >
<input matSliderThumb [(ngModel)]="data.settings.thresholdMax" /> <input matSliderThumb [formControl]="thresholdMaxControl" />
</mat-slider> </mat-slider>
<gf-value <gf-value
[isPercent]="data.rule.configuration.threshold.unit === '%'" [isPercent]="data.rule.configuration.threshold.unit === '%'"
@ -131,7 +131,8 @@
<button <button
color="primary" color="primary"
mat-flat-button mat-flat-button
(click)="dialogRef.close(data.settings)" [disabled]="!isDirty"
(click)="onSave()"
> >
<ng-container i18n>Save</ng-container> <ng-container i18n>Save</ng-container>
</button> </button>

Loading…
Cancel
Save