From 12070c4e9c6c6a2d8e0ea11459abb35e0df35d81 Mon Sep 17 00:00:00 2001 From: Brandon Clayton <bclayton@usgs.gov> Date: Thu, 18 Jul 2024 16:05:19 -0600 Subject: [PATCH] switch to signals and reactive forms --- .../aws/check-haz-jobs/state/app.facade.ts | 2 +- .../terminate-haz-jobs/state/app.facade.ts | 2 +- .../hanging-wall-effects/state/app.facade.ts | 12 ++ .../math/exceedance-explorer/app.component.ts | 22 +-- .../control-panel.component.html | 174 +++++++++--------- .../control-panel/control-panel.component.ts | 13 +- .../components/plot/plot.component.html | 8 +- .../components/plot/plot.component.ts | 7 +- .../settings/settings.component.html | 18 +- .../components/settings/settings.component.ts | 34 +++- .../exceedance-explorer/state/app.actions.ts | 13 -- .../exceedance-explorer/state/app.effects.ts | 40 ---- .../exceedance-explorer/state/app.facade.ts | 78 ++++---- .../exceedance-explorer/state/app.reducer.ts | 84 --------- .../exceedance-explorer/state/app.state.ts | 55 +++--- .../utils/app.default-values.ts | 13 +- .../exceedance-explorer/utils/app.utils.ts | 11 +- .../utils/control-panel.validators.ts | 33 +++- .../src/app/dev/math/math.routes.ts | 9 - 19 files changed, 248 insertions(+), 380 deletions(-) delete mode 100644 projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.actions.ts delete mode 100644 projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.effects.ts delete mode 100644 projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.reducer.ts diff --git a/projects/nshmp-apps/src/app/dev/aws/check-haz-jobs/state/app.facade.ts b/projects/nshmp-apps/src/app/dev/aws/check-haz-jobs/state/app.facade.ts index e2ca964f1..c400e0df2 100644 --- a/projects/nshmp-apps/src/app/dev/aws/check-haz-jobs/state/app.facade.ts +++ b/projects/nshmp-apps/src/app/dev/aws/check-haz-jobs/state/app.facade.ts @@ -158,7 +158,7 @@ export class AppFacade { */ private callTerminateJobService(): void { this.awsService - .callTerminateJobServiceEffect( + .callTerminateJobService( `${this.baseUrl}/${this.terminateService}`, this.formGroup.controls.id ) diff --git a/projects/nshmp-apps/src/app/dev/aws/terminate-haz-jobs/state/app.facade.ts b/projects/nshmp-apps/src/app/dev/aws/terminate-haz-jobs/state/app.facade.ts index 99aad0774..ee00158d1 100644 --- a/projects/nshmp-apps/src/app/dev/aws/terminate-haz-jobs/state/app.facade.ts +++ b/projects/nshmp-apps/src/app/dev/aws/terminate-haz-jobs/state/app.facade.ts @@ -86,7 +86,7 @@ export class AppFacade { */ callTerminateJobService(): void { this.awsService - .callTerminateJobServiceEffect( + .callTerminateJobService( `${this.baseUrl}/${this.terminateService}`, this.formGroup.controls.id ) diff --git a/projects/nshmp-apps/src/app/dev/gmm/hanging-wall-effects/state/app.facade.ts b/projects/nshmp-apps/src/app/dev/gmm/hanging-wall-effects/state/app.facade.ts index e1788aeb6..af9bb86db 100644 --- a/projects/nshmp-apps/src/app/dev/gmm/hanging-wall-effects/state/app.facade.ts +++ b/projects/nshmp-apps/src/app/dev/gmm/hanging-wall-effects/state/app.facade.ts @@ -24,6 +24,7 @@ import { initialState, usageFormValues, } from './app.state'; +import {redrawPlots} from 'projects/nshmp-apps/src/shared/utils/facade.utils'; @Injectable({providedIn: 'root'}) export class AppFacade { @@ -107,6 +108,15 @@ export class AppFacade { .subscribe(usageResponse => this.handleUsageResponse(usageResponse)); } + /** + * Redraw the plot. + */ + plotRedraw(): void { + this.updateState({ + plots: redrawPlots(this.state().plots), + }); + } + /** * Reset the control panel. */ @@ -122,6 +132,8 @@ export class AppFacade { resetPlotSettings(): void { this.state().plots.forEach((plot, id) => { const defaultSettings = defaultPlots().get(id).settingsForm; + plot.settingsForm.markAsPristine(); + plot.settingsForm.markAsUntouched(); plot.settingsForm.patchValue(defaultSettings.getRawValue()); }); } diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/app.component.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/app.component.ts index 338c9b088..2beeaa47f 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/app.component.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/app.component.ts @@ -1,11 +1,10 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; +import {Component} from '@angular/core'; import {NshmpLibNgAboutPageComponent} from '@ghsc/nshmp-lib-ng/about'; import {NshmpLibNgTemplateComponent} from '@ghsc/nshmp-lib-ng/nshmp'; import { NshmpTemplateContentContainerComponent, NshmpTemplateControlPanelComponent, NshmpTemplatePlotContentComponent, - NshmpTemplateService, NshmpTemplateSettingsComponent, } from '@ghsc/nshmp-template'; import {devApps} from 'projects/nshmp-apps/src/shared/utils/applications.utils'; @@ -16,7 +15,6 @@ import {AboutComponent} from './components/about/about.component'; import {ControlPanelComponent} from './components/control-panel/control-panel.component'; import {PlotComponent} from './components/plot/plot.component'; import {SettingsComponent} from './components/settings/settings.component'; -import {ExceedanceExplorerFacade} from './state/app.facade'; /** * Entrypoint for exceedance explorer development application. @@ -41,27 +39,11 @@ import {ExceedanceExplorerFacade} from './state/app.facade'; styleUrl: './app.component.scss', templateUrl: './app.component.html', }) -export class AppComponent implements OnInit, OnDestroy { +export class AppComponent { /** Navigation list for menu */ navigationList = devNavigation(); /** Small screen subscription */ smallScreenSubscription: Subscription; /** Application title */ title = devApps().exceedanceExplorer.display; - - constructor( - private facade: ExceedanceExplorerFacade, - private nshmpTemplateService: NshmpTemplateService - ) {} - - ngOnInit(): void { - this.smallScreenSubscription = - this.nshmpTemplateService.isSmallScreen$.subscribe(() => - this.facade.plotRedraw() - ); - } - - ngOnDestroy(): void { - this.smallScreenSubscription.unsubscribe(); - } } diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.html index 31a8c63cc..f5494ebb2 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.html +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.html @@ -1,94 +1,92 @@ <!-- Exceedance Control Panel --> -@if (form$ | async; as form) { - <form - class="height-full overflow-auto" - [ngrxFormState]="form" - (submit)="onSubmit()" - > - <!-- Median --> - <mat-form-field class="grid-col-12 median-input padding-top-1"> - <mat-label> Median (g) </mat-label> - <input - type="number" - matInput - [ngrxFormControlState]="form?.controls?.median" - [step]="formBounds.median.step" - [min]="formBounds.median.min" - [max]="formBounds.median.max" - /> - <mat-error> - [ {{ formBounds.median.min }}, {{ formBounds.median.max }} ] - </mat-error> - </mat-form-field> - - <!-- Sigma --> - <mat-form-field class="grid-col-12 sigma-input"> - <mat-label> Sigma (natural log units) </mat-label> - <input - type="number" - matInput - [ngrxFormControlState]="form?.controls?.sigma" - [step]="formBounds.sigma.step" - [min]="formBounds.sigma.min" - [max]="formBounds.sigma.max" - /> - <mat-error> - [ {{ formBounds.sigma.min }}, {{ formBounds.sigma.max }} ] - </mat-error> - </mat-form-field> +<form + class="height-full overflow-auto" + [formGroup]="form" + (submit)="onSubmit()" +> + <!-- Median --> + <mat-form-field class="grid-col-12 median-input padding-top-1"> + <mat-label> Median (g) </mat-label> + <input + type="number" + matInput + [formControl]="form.controls.median" + [step]="formBounds.median.step" + [min]="formBounds.median.min" + [max]="formBounds.median.max" + /> + <mat-error> + [ {{ formBounds.median.min }}, {{ formBounds.median.max }} ] + </mat-error> + </mat-form-field> - <!-- Rate --> - <mat-form-field class="grid-col-12 rate-input"> - <mat-label> Rate </mat-label> - <input - type="number" - matInput - [ngrxFormControlState]="form?.controls?.rate" - [step]="formBounds.rate.step" - [min]="formBounds.rate.min" - [max]="formBounds.rate.max" - /> - <mat-error> - [ {{ formBounds.rate.min }}, {{ formBounds.rate.max }} ] - </mat-error> - </mat-form-field> + <!-- Sigma --> + <mat-form-field class="grid-col-12 sigma-input"> + <mat-label> Sigma (natural log units) </mat-label> + <input + type="number" + matInput + [formControl]="form.controls.sigma" + [step]="formBounds.sigma.step" + [min]="formBounds.sigma.min" + [max]="formBounds.sigma.max" + /> + <mat-error> + [ {{ formBounds.sigma.min }}, {{ formBounds.sigma.max }} ] + </mat-error> + </mat-form-field> - <!-- Truncation --> - <mat-checkbox - color="primary" - class="margin-left-1 truncation-checkbox" - [ngrxFormControlState]="form?.controls?.truncation" - > - Truncation - </mat-checkbox> + <!-- Rate --> + <mat-form-field class="grid-col-12 rate-input"> + <mat-label> Rate </mat-label> + <input + type="number" + matInput + [formControl]="form.controls.rate" + [step]="formBounds.rate.step" + [min]="formBounds.rate.min" + [max]="formBounds.rate.max" + /> + <mat-error> + [ {{ formBounds.rate.min }}, {{ formBounds.rate.max }} ] + </mat-error> + </mat-form-field> - <!-- Truncation Level --> - <mat-form-field class="grid-col-12 truncation-level-input"> - <mat-label> Truncation Level (n) </mat-label> - <input - [disabled]="form.value.truncation === false" - type="number" - matInput - [ngrxFormControlState]="form?.controls?.truncationLevel" - [step]="formBounds.truncationLevel.step" - [min]="formBounds.truncationLevel.min" - [max]="formBounds.truncationLevel.max" - /> - <mat-error> - [ {{ formBounds.truncationLevel.min }}, - {{ formBounds.truncationLevel.max }} ] - </mat-error> - </mat-form-field> + <!-- Truncation --> + <mat-checkbox + color="primary" + class="margin-left-1 truncation-checkbox" + [formControl]="form.controls.truncation" + > + Truncation + </mat-checkbox> - <nshmp-lib-ng-control-panel-buttons - resetButtonSize="grid-col-5" - [resetDisabled]="(data$ | async)?.length === 0" - resetLabel="Clear Plot" - plotButtonSize="grid-col-5" - [plotDisabled]="form?.isInvalid" - plotLabel="Add Curve" - [showServiceCallInfo]="false" - (resetClick)="facade.clearPlot()" + <!-- Truncation Level --> + <mat-form-field class="grid-col-12 truncation-level-input"> + <mat-label> Truncation Level (n) </mat-label> + <input + [disabled]="form.value.truncation === false" + type="number" + matInput + [formControl]="form.controls.truncationLevel" + [step]="formBounds.truncationLevel.step" + [min]="formBounds.truncationLevel.min" + [max]="formBounds.truncationLevel.max" /> - </form> -} + <mat-error> + [ {{ formBounds.truncationLevel.min }}, + {{ formBounds.truncationLevel.max }} ] + </mat-error> + </mat-form-field> + + <nshmp-lib-no-ngrx-control-panel-buttons + resetButtonSize="grid-col-5" + [resetDisabled]="data()?.length === 0" + resetLabel="Clear Plot" + plotButtonSize="grid-col-5" + [plotDisabled]="form.invalid" + plotLabel="Add Curve" + [showServiceCallInfo]="false" + (resetClick)="facade.clearPlot()" + /> +</form> diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.ts index bc088b2df..af462349c 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/control-panel/control-panel.component.ts @@ -1,14 +1,13 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; import {MatCheckbox} from '@angular/material/checkbox'; import {MatError, MatFormField, MatLabel} from '@angular/material/form-field'; import {MatInput} from '@angular/material/input'; import { NshmpLibNgControlPanelButtonsComponent, - NshmpNgrxFormsModule, NshmpService, -} from '@ghsc/nshmp-lib-ng/nshmp'; -import {map} from 'rxjs/operators'; +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import {ExceedanceExplorerFacade} from '../../state/app.facade'; import {EXCEEDANCE_FORM_BOUNDS} from '../../utils/app.utils'; @@ -25,7 +24,7 @@ import {EXCEEDANCE_FORM_BOUNDS} from '../../utils/app.utils'; MatCheckbox, AsyncPipe, NshmpLibNgControlPanelButtonsComponent, - NshmpNgrxFormsModule, + ReactiveFormsModule, ], selector: 'app-control-panel', standalone: true, @@ -37,9 +36,9 @@ export class ControlPanelComponent { formBounds = EXCEEDANCE_FORM_BOUNDS; /** Plot data state */ - data$ = this.facade.plot$.pipe(map(plot => plot.plotData.data)); + data = computed(() => this.facade.plot().plotData.data); /** Form field state */ - form$ = this.facade.form$; + form = this.facade.formGroup; constructor( public facade: ExceedanceExplorerFacade, diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.html b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.html index ab081d26a..a797ea077 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.html +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.html @@ -1,5 +1,5 @@ <!-- Exceedance Plot --> -<nshmp-lib-ng-plots-container> +<nshmp-lib-no-ngrx-plots-container> <mat-accordion multi> <!-- Plot --> <mat-expansion-panel expanded> @@ -9,9 +9,7 @@ <mat-divider /> - @if (plotData$ | async; as plot) { - <nshmp-lib-ng-plot class="exceedance-plot" [plot]="plot" /> - } + <nshmp-lib-no-ngrx-plot class="exceedance-plot" [plot]="plotData()" /> </mat-expansion-panel> </mat-accordion> -</nshmp-lib-ng-plots-container> +</nshmp-lib-no-ngrx-plots-container> diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.ts index 0bf1217e4..0b35ab6af 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/plot/plot.component.ts @@ -1,5 +1,5 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import {MatDivider} from '@angular/material/divider'; import { MatAccordion, @@ -10,8 +10,7 @@ import { import { NshmpLibNgPlotComponent, NshmpLibNgPlotsContainerComponent, -} from '@ghsc/nshmp-lib-ng/plot'; -import {map} from 'rxjs/operators'; +} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {ExceedanceExplorerFacade} from '../../state/app.facade'; @@ -37,7 +36,7 @@ import {ExceedanceExplorerFacade} from '../../state/app.facade'; templateUrl: './plot.component.html', }) export class PlotComponent { - plotData$ = this.facade.plot$.pipe(map(plot => plot.plotData)); + plotData = computed(() => this.facade.plot().plotData); constructor(private facade: ExceedanceExplorerFacade) {} } diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.html b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.html index 45866e00f..a912ed2ee 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.html +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.html @@ -1,20 +1,16 @@ <div class="height-full overflow-auto"> <!-- Exceedance plot settings --> - @if (plot$ | async) { - <nshmp-lib-ng-plot-settings - class="exceedance-settings" - [plot]="plot$ | async" - /> - } + <nshmp-lib-no-ngrx-plot-settings + class="exceedance-settings" + [plot]="plot()" + (updatedPlot)="updatePlot($event)" + /> <div class="padding-y-3"></div> <!-- Reset button --> - <nshmp-lib-ng-plot-reset-plot-settings + <nshmp-lib-no-ngrx-plot-reset-plot-settings (resetClick)="facade.resetSettings()" - [resetDisabled]=" - (plot$ | async)?.settingsForm?.isPristine && - (plot$ | async)?.settingsForm?.isUntouched - " + [resetDisabled]="resetDisabled" /> </div> diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.ts index a73f05868..bdb3cab65 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/components/settings/settings.component.ts @@ -1,10 +1,13 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, OnDestroy} from '@angular/core'; import { NshmpLibNgPlotResetPlotSettingsComponent, NshmpLibNgPlotSettingsComponent, -} from '@ghsc/nshmp-lib-ng/plot'; + NshmpPlot, +} from '@ghsc/nshmp-lib-no-ngrx/plot'; +import {Subscription} from 'rxjs'; +import {Plots} from '../../models/plots.model'; import {ExceedanceExplorerFacade} from '../../state/app.facade'; /** @@ -21,8 +24,31 @@ import {ExceedanceExplorerFacade} from '../../state/app.facade'; styleUrl: './settings.component.scss', templateUrl: './settings.component.html', }) -export class SettingsComponent { - plot$ = this.facade.plot$; +export class SettingsComponent implements OnDestroy { + plot = computed(() => { + this.settingsFormSubscription?.unsubscribe(); + const plot = this.facade.plot(); + + this.settingsFormSubscription = plot.settingsForm.valueChanges.subscribe( + () => + (this.resetDisabled = + plot.settingsForm.pristine && plot.settingsForm.untouched) + ); + + return plot; + }); + + resetDisabled = true; + + private settingsFormSubscription = new Subscription(); constructor(public facade: ExceedanceExplorerFacade) {} + + ngOnDestroy(): void { + this.settingsFormSubscription.unsubscribe(); + } + + updatePlot(plot: NshmpPlot): void { + this.facade.updatePlot(plot, Plots.EXCEEDANCE); + } } diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.actions.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.actions.ts deleted file mode 100644 index 0aeb3b54e..000000000 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.actions.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {createActionGroup, emptyProps} from '@ngrx/store'; -import {sharedActions} from 'projects/nshmp-apps/src/shared/state/shared'; - -export const actions = createActionGroup({ - events: { - ...sharedActions, - /** Calculate exceedance action */ - 'Calculate Exceedance': emptyProps(), - /** Clear plots action */ - 'Clear Plot': emptyProps(), - }, - source: 'Math Exceedance Explorer Development App', -}); diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.effects.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.effects.ts deleted file mode 100644 index 8628ba989..000000000 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.effects.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {Injectable} from '@angular/core'; -import {NshmpService} from '@ghsc/nshmp-lib-ng/nshmp'; -import {Actions, createEffect, ofType} from '@ngrx/effects'; -import {concatLatestFrom} from '@ngrx/operators'; -import {Store} from '@ngrx/store'; -import {catchError, map} from 'rxjs/operators'; - -import {exceedanceAddPlotData} from '../utils/app.utils'; -import {actions} from './app.actions'; -import {exceedanceExplorerAppFeature} from './app.reducer'; - -/** - * NGRX effects for exceedance explorer application. - */ -@Injectable() -export class ExceedanceExplorerAppEffects { - /** - * Calculate exceedance and add new plot data. - */ - calculateExceedance$ = createEffect(() => - this.actions$.pipe( - ofType(actions.calculateExceedance), - concatLatestFrom(() => - this.store.select( - exceedanceExplorerAppFeature.selectExceedanceExplorerState - ) - ), - map(([, state]) => { - return actions.plots({plots: exceedanceAddPlotData(state)}); - }), - catchError((error: Error) => this.nshmpService.throwError$(error)) - ) - ); - - constructor( - private actions$: Actions, - private store: Store, - private nshmpService: NshmpService - ) {} -} diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.facade.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.facade.ts index ecf9d0606..6c8fb6f5f 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.facade.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.facade.ts @@ -1,12 +1,16 @@ -import {Injectable} from '@angular/core'; -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; -import {select, Store} from '@ngrx/store'; -import {FormGroupState} from 'ngrx-forms'; -import {Observable} from 'rxjs'; +import {computed, Injectable, Signal, signal} from '@angular/core'; +import {FormBuilder} from '@angular/forms'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {ControlForm} from '../models/control-form.model'; -import {actions} from './app.actions'; -import {exceedanceExplorerAppFeature} from './app.reducer'; +import {Plots} from '../models/plots.model'; +import { + EXCEEDANCE_EXPLORER_DEFAULT_FORM_VALUES, + exceedanceExplorerDefaultPlots, +} from '../utils/app.default-values'; +import {exceedanceAddPlotData} from '../utils/app.utils'; +import {addValidators} from '../utils/control-panel.validators'; +import {AppState, EXCEEDANCE_EXPLORER_INTIAL_STATE} from './app.state'; /** * Entrypoint for accessing NGRX store. @@ -15,58 +19,64 @@ import {exceedanceExplorerAppFeature} from './app.reducer'; providedIn: 'root', }) export class ExceedanceExplorerFacade { - constructor(private store: Store) {} + /** Control panels forms */ + readonly formGroup = this.formBuilder.group<ControlForm>( + EXCEEDANCE_EXPLORER_DEFAULT_FORM_VALUES + ); - /** - * Returns the control panel form. - */ - get form$(): Observable<FormGroupState<ControlForm>> { - return this.store.pipe( - select(exceedanceExplorerAppFeature.selectControlForm) - ); + private state = signal<AppState>(EXCEEDANCE_EXPLORER_INTIAL_STATE); + + constructor(private formBuilder: FormBuilder) { + addValidators(this.formGroup); } /** * Returns the exceedance plot. */ - get plot$(): Observable<NshmpPlot> { - return this.store.pipe( - select(exceedanceExplorerAppFeature.selectExceedancePlot) - ); + get plot(): Signal<NshmpPlot> { + return computed(() => this.state().plots.get(Plots.EXCEEDANCE)); } /** * Calculate exceedance. */ calculateExceedance(): void { - this.store.dispatch(actions.calculateExceedance()); + this.state.set({ + plots: exceedanceAddPlotData(this.state(), this.formGroup), + }); } /** * Clear the plot. */ clearPlot(): void { - this.store.dispatch(actions.clearPlot()); + this.state.set(EXCEEDANCE_EXPLORER_INTIAL_STATE); } /** - * Initialize exceedance application. - */ - init(): void { - this.store.dispatch(actions.init()); - } - - /** - * Redraw the plot. + * Reset the plot settings. */ - plotRedraw(): void { - this.store.dispatch(actions.plotRedraw()); + resetSettings(): void { + this.state().plots.forEach((plot, id) => { + const defaultSettings = + exceedanceExplorerDefaultPlots().get(id).settingsForm; + plot.settingsForm.markAsUntouched(); + plot.settingsForm.markAsPristine(); + plot.settingsForm.patchValue(defaultSettings.getRawValue()); + }); } /** - * Reset the plot settings. + * Update a single plot in the state. + * + * @param plot Updated plot + * @param id Id of plot to update */ - resetSettings(): void { - this.store.dispatch(actions.resetSettings()); + updatePlot(plot: NshmpPlot, id: string): void { + const plots = this.state().plots; + plots.set(id, plot); + this.state.set({ + plots, + }); } } diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.reducer.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.reducer.ts deleted file mode 100644 index 993bc6411..000000000 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.reducer.ts +++ /dev/null @@ -1,84 +0,0 @@ -import {createFeature, createReducer, createSelector, on} from '@ngrx/store'; -import { - onNgrxForms, - onNgrxFormsAction, - SetValueAction, - wrapReducerWithFormStateUpdate, -} from 'ngrx-forms'; -import { - onPlotRedraw, - onPlots, - onPlotSettingsForm, - onResetSetting, -} from 'projects/nshmp-apps/src/shared/state/shared'; - -import {Plots} from '../models/plots.model'; -import { - EXCEEDANCE_SETTINGS_ID, - exceedanceExplorerDefaultPlots, -} from '../utils/app.default-values'; -import {validateControlPanelForm} from '../utils/control-panel.validators'; -import {actions} from './app.actions'; -import {EXCEEDANCE_EXPLORER_INTIAL_STATE} from './app.state'; - -/** - * Exceedance explorer NGRX feature. - */ -export const exceedanceExplorerAppFeature = createFeature({ - extraSelectors: ({selectPlots}) => ({ - selectExceedancePlot: createSelector(selectPlots, plots => - plots.get(Plots.EXCEEDANCE) - ), - }), - name: 'exceedanceExplorer', - reducer: createReducer( - /** Initial state */ - EXCEEDANCE_EXPLORER_INTIAL_STATE, - /* On NGRX forms */ - onNgrxForms(), - /* On NGRX form change */ - onNgrxFormsAction(SetValueAction, (state, action) => { - if (action.controlId.includes(EXCEEDANCE_SETTINGS_ID)) { - return onPlotSettingsForm(state, action); - } else { - return { - ...state, - }; - } - }), - /* On init action */ - on(actions.init, () => { - return { - ...EXCEEDANCE_EXPLORER_INTIAL_STATE, - }; - }), - /* On plots action */ - on(actions.plots, (state, {plots}) => { - return onPlots(state, plots); - }), - /* On plot redraw action */ - on(actions.plotRedraw, state => { - return onPlotRedraw(state); - }), - /* On reset settings action */ - on(actions.resetSettings, state => { - return onResetSetting(state, exceedanceExplorerDefaultPlots()); - }), - /* On clear plot action */ - on(actions.clearPlot, state => { - return { - ...state, - plots: exceedanceExplorerDefaultPlots(), - }; - }) - ), -}); - -/** - * Application NGRX reducer with validators. - */ -exceedanceExplorerAppFeature.reducer = wrapReducerWithFormStateUpdate( - exceedanceExplorerAppFeature.reducer, - state => state.controlForm, - validateControlPanelForm -); diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.state.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.state.ts index f16a2fbef..96f22f248 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.state.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/state/app.state.ts @@ -1,51 +1,38 @@ -import { - createFormGroupState, - FormGroupState, - setUserDefinedProperty, - updateGroup, -} from 'ngrx-forms'; -import {SharedAppState} from 'projects/nshmp-apps/src/shared/state/shared'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; -import {ControlForm} from '../models/control-form.model'; -import { - EXCEEDANCE_EXPLORER_DEFAULT_FORM_VALUES, - exceedanceExplorerDefaultPlots, -} from '../utils/app.default-values'; -import {EXCEEDANCE_FORM_BOUNDS} from '../utils/app.utils'; +import {exceedanceExplorerDefaultPlots} from '../utils/app.default-values'; export const EXCEEDANCE_EXPLORER_FORM_ID = 'ngrx-forms [Exceedance Explorer Control Form]'; -/** - * Inital form state for exceedance control panel. - */ -export const EXCEEDANCE_EXPLORER_INTITAL_FORM_STATE = updateGroup<ControlForm>( - createFormGroupState<ControlForm>(EXCEEDANCE_EXPLORER_FORM_ID, { - ...EXCEEDANCE_EXPLORER_DEFAULT_FORM_VALUES, - }), - { - median: setUserDefinedProperty('bounds', EXCEEDANCE_FORM_BOUNDS.median), - rate: setUserDefinedProperty('bounds', EXCEEDANCE_FORM_BOUNDS.rate), - sigma: setUserDefinedProperty('bounds', EXCEEDANCE_FORM_BOUNDS.sigma), - truncationLevel: setUserDefinedProperty( - 'bounds', - EXCEEDANCE_FORM_BOUNDS.truncationLevel - ), - } -); +// /** +// * Inital form state for exceedance control panel. +// */ +// export const EXCEEDANCE_EXPLORER_INTITAL_FORM_STATE = updateGroup<ControlForm>( +// createFormGroupState<ControlForm>(EXCEEDANCE_EXPLORER_FORM_ID, { +// ...EXCEEDANCE_EXPLORER_DEFAULT_FORM_VALUES, +// }), +// { +// median: setUserDefinedProperty('bounds', EXCEEDANCE_FORM_BOUNDS.median), +// rate: setUserDefinedProperty('bounds', EXCEEDANCE_FORM_BOUNDS.rate), +// sigma: setUserDefinedProperty('bounds', EXCEEDANCE_FORM_BOUNDS.sigma), +// truncationLevel: setUserDefinedProperty( +// 'bounds', +// EXCEEDANCE_FORM_BOUNDS.truncationLevel +// ), +// } +// ); /** * The exceedance app state. */ -export interface AppState extends SharedAppState { - /** Control form state */ - controlForm: FormGroupState<ControlForm>; +export interface AppState { + plots: Map<string, NshmpPlot>; } /** * Initial exceedance app state. */ export const EXCEEDANCE_EXPLORER_INTIAL_STATE: AppState = { - controlForm: EXCEEDANCE_EXPLORER_INTITAL_FORM_STATE, plots: exceedanceExplorerDefaultPlots(), }; diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/app.default-values.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/app.default-values.ts index 170519b50..02c96828a 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/app.default-values.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/app.default-values.ts @@ -3,15 +3,11 @@ import { NshmpPlotSettings, PlotOptions, plotUtils, -} from '@ghsc/nshmp-lib-ng/plot'; -import {createFormGroupState} from 'ngrx-forms'; +} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {ControlForm} from '../models/control-form.model'; import {Plots} from '../models/plots.model'; -export const EXCEEDANCE_SETTINGS_ID = - 'ngrx-forms [Exceedance Explorer Settings Form]'; - /** * Exceedance default control panel form values. */ @@ -31,12 +27,7 @@ export function exceedanceExplorerDefaultPlots(): Map<string, NshmpPlot> { plots.set(Plots.EXCEEDANCE, { label: 'Exceedance', plotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - EXCEEDANCE_SETTINGS_ID, - { - ...settingsForm, - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup(settingsForm), }); return plots; } diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/app.utils.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/app.utils.ts index 7250c0e0c..ae0272f5a 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/app.utils.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/app.utils.ts @@ -1,5 +1,5 @@ -import {NumberBounds} from '@ghsc/nshmp-lib-ng/nshmp'; -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; +import {FormGroupControls, NumberBounds} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {ExceedanceModel, Maths} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/calc'; import {UncertaintyModel} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/data'; import * as d3 from 'd3-array'; @@ -69,8 +69,11 @@ export const EXCEEDANCE_X_VALUES: number[] = d3.ticks( * * @param state The current state */ -export function exceedanceAddPlotData(state: AppState): Map<string, NshmpPlot> { - const exceedanceData = calculateExceedance(state.controlForm.value); +export function exceedanceAddPlotData( + state: AppState, + formGroup: FormGroupControls<ControlForm> +): Map<string, NshmpPlot> { + const exceedanceData = calculateExceedance(formGroup.getRawValue()); const plots = new Map<string, NshmpPlot>(); const plot = state.plots.get(Plots.EXCEEDANCE); const data = [...plot.plotData.data]; diff --git a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/control-panel.validators.ts b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/control-panel.validators.ts index 34b866adc..7128d96f3 100644 --- a/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/control-panel.validators.ts +++ b/projects/nshmp-apps/src/app/dev/math/exceedance-explorer/utils/control-panel.validators.ts @@ -1,16 +1,29 @@ -import {nshmpUtils} from '@ghsc/nshmp-lib-ng/nshmp'; -import {updateGroup, validate} from 'ngrx-forms'; -import {required} from 'ngrx-forms/validation'; +import {FormControl, Validators} from '@angular/forms'; +import {FormGroupControls} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import {ControlForm} from '../models/control-form.model'; +import {EXCEEDANCE_FORM_BOUNDS, FormBounds} from './app.utils'; /** * Validate control panel form. */ -export const validateControlPanelForm = updateGroup<ControlForm>({ - median: nshmpUtils.validateBounds, - rate: nshmpUtils.validateBounds, - sigma: nshmpUtils.validateBounds, - truncation: validate([required]), - truncationLevel: nshmpUtils.validateBounds, -}); +export function addValidators(formGroup: FormGroupControls<ControlForm>): void { + boundsValidators(formGroup.controls.median, EXCEEDANCE_FORM_BOUNDS.median); + boundsValidators(formGroup.controls.rate, EXCEEDANCE_FORM_BOUNDS.rate); + boundsValidators(formGroup.controls.sigma, EXCEEDANCE_FORM_BOUNDS.sigma); + boundsValidators( + formGroup.controls.truncationLevel, + EXCEEDANCE_FORM_BOUNDS.truncationLevel + ); + formGroup.controls.truncation.addValidators(control => + Validators.required(control) + ); +} + +function boundsValidators(control: FormControl, bounds: FormBounds): void { + control.addValidators([ + Validators.min(bounds.min), + Validators.max(bounds.max), + c => Validators.required(c), + ]); +} diff --git a/projects/nshmp-apps/src/app/dev/math/math.routes.ts b/projects/nshmp-apps/src/app/dev/math/math.routes.ts index 7568c2d8f..ce59f47f5 100644 --- a/projects/nshmp-apps/src/app/dev/math/math.routes.ts +++ b/projects/nshmp-apps/src/app/dev/math/math.routes.ts @@ -1,9 +1,4 @@ import {Routes} from '@angular/router'; -import {provideEffects} from '@ngrx/effects'; -import {provideState} from '@ngrx/store'; - -import {ExceedanceExplorerAppEffects} from './exceedance-explorer/state/app.effects'; -import {exceedanceExplorerAppFeature} from './exceedance-explorer/state/app.reducer'; /** Routes for math applications */ const routes: Routes = [ @@ -13,10 +8,6 @@ const routes: Routes = [ com => com.AppComponent ), path: 'exceedance-explorer', - providers: [ - provideState(exceedanceExplorerAppFeature), - provideEffects(ExceedanceExplorerAppEffects), - ], }, ]; -- GitLab