From 72bc6cbf955c72030178484df9ac386074975d16 Mon Sep 17 00:00:00 2001 From: Brandon Clayton <bclayton@usgs.gov> Date: Thu, 25 Jul 2024 15:55:43 -0600 Subject: [PATCH 1/4] remove ngrx --- .../src/app/gmm/distance/state/app.actions.ts | 22 -- .../src/app/gmm/distance/state/app.effects.ts | 134 --------- .../src/app/gmm/distance/state/app.reducer.ts | 281 ------------------ projects/nshmp-apps/src/app/gmm/gmm.routes.ts | 6 - 4 files changed, 443 deletions(-) delete mode 100644 projects/nshmp-apps/src/app/gmm/distance/state/app.actions.ts delete mode 100644 projects/nshmp-apps/src/app/gmm/distance/state/app.effects.ts delete mode 100644 projects/nshmp-apps/src/app/gmm/distance/state/app.reducer.ts diff --git a/projects/nshmp-apps/src/app/gmm/distance/state/app.actions.ts b/projects/nshmp-apps/src/app/gmm/distance/state/app.actions.ts deleted file mode 100644 index ec9233dce..000000000 --- a/projects/nshmp-apps/src/app/gmm/distance/state/app.actions.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - GmmDistanceResponse, - GmmDistanceUsage, -} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; -import {createActionGroup, emptyProps, props} from '@ngrx/store'; -import {sharedActions} from 'projects/nshmp-apps/src/shared/state/shared'; - -/** - * GMM distance NGRX actions. - */ -export const actions = createActionGroup({ - events: { - ...sharedActions, - /** Action to set the initial form values from query or default */ - 'Initial Form Set': emptyProps(), - /** Service response action */ - 'Service Response': props<{serviceResponses: GmmDistanceResponse[]}>(), - /** Usage response action */ - 'Usage Response': props<{usageResponses: GmmDistanceUsage}>(), - }, - source: 'GMM vs Distance', -}); diff --git a/projects/nshmp-apps/src/app/gmm/distance/state/app.effects.ts b/projects/nshmp-apps/src/app/gmm/distance/state/app.effects.ts deleted file mode 100644 index 8a5dfef38..000000000 --- a/projects/nshmp-apps/src/app/gmm/distance/state/app.effects.ts +++ /dev/null @@ -1,134 +0,0 @@ -import {Injectable} from '@angular/core'; -import {gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; -import {NshmpService, SpinnerService} from '@ghsc/nshmp-lib-ng/nshmp'; -import {plotUtils} from '@ghsc/nshmp-lib-ng/plot'; -import { - GmmDistanceResponse, - GmmDistanceUsage, -} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; -import {Actions, createEffect, ofType} from '@ngrx/effects'; -import {concatLatestFrom} from '@ngrx/operators'; -import {Store} from '@ngrx/store'; -import {environment} from 'projects/nshmp-apps/src/environments/environment'; -import {catchError, exhaustMap, map, mergeMap} from 'rxjs/operators'; - -import {defaultFormValues} from '../utils/app.default-values'; -import {serviceResponseToPlotData} from '../utils/response.handler'; -import {actions} from './app.actions'; -import {distanceAppFeature} from './app.reducer'; - -/** - * NGRX application effects. - */ -@Injectable() -export class GmmDistanceAppEffects { - /** nshmp-ws base URL */ - baseUrl = environment.webServices.data.url; - /** GMM service URL */ - serviceUrl = `${this.baseUrl}${environment.webServices.data.services.gmmDistance}`; - - /** - * Call the services. - */ - callService$ = createEffect(() => - this.actions$.pipe( - ofType(actions.callServices), - concatLatestFrom(() => - this.store.select(distanceAppFeature.selectGmmDistanceAppState) - ), - exhaustMap(([, state]) => { - this.spinnerService.show(SpinnerService.MESSAGE_SERVICE); - - const urls = gmmUtils.serviceEndpoints( - this.serviceUrl, - state.controlForm.value, - state.controlForm.value.multiSelectableParam - ); - - return this.nshmpService.callServices$(urls).pipe( - mergeMap((serviceResponses: GmmDistanceResponse[]) => { - this.spinnerService.remove(); - return [actions.serviceResponse({serviceResponses})]; - }), - catchError((error: Error) => this.nshmpService.throwError$(error)) - ); - }), - catchError((error: Error) => this.nshmpService.throwError$(error)) - ) - ); - - /** - * Transform the service responses to plot data. - */ - createData$ = createEffect(() => - this.actions$.pipe( - ofType(actions.serviceResponse), - concatLatestFrom(() => - this.store.select(distanceAppFeature.selectGmmDistanceAppState) - ), - map(([, state]) => { - const plots = plotUtils.updateAppPlotSettings( - serviceResponseToPlotData(state) - ); - return actions.plots({plots}); - }), - catchError((error: Error) => this.nshmpService.throwError$(error)) - ) - ); - - /** - * Initialize the application. - */ - init$ = createEffect(() => - this.actions$.pipe( - ofType(actions.init), - concatLatestFrom(() => - this.store.select(distanceAppFeature.selectControlForm) - ), - exhaustMap(([, form]) => { - this.spinnerService.show(SpinnerService.MESSAGE_METADATA); - - return this.nshmpService - .callService$<GmmDistanceUsage>(this.serviceUrl) - .pipe( - mergeMap(usage => { - this.spinnerService.remove(); - const parameters = usage.response.parameters; - const values = defaultFormValues(parameters); - const controls = form.controls; - - const typedActions = gmmUtils.initialFormSetActions( - form, - values, - parameters - ); - typedActions.push(actions.usageResponse({usageResponses: usage})); - gmmUtils.addFormAction( - parameters.rMax, - controls.rMax.id, - values.rMax, - typedActions - ); - gmmUtils.addFormAction( - parameters.rMin, - controls.rMin.id, - values.rMin, - typedActions - ); - - return typedActions; - }), - catchError((error: Error) => this.nshmpService.throwError$(error)) - ); - }), - catchError((error: Error) => this.nshmpService.throwError$(error)) - ) - ); - - constructor( - private actions$: Actions, - private nshmpService: NshmpService, - private spinnerService: SpinnerService, - private store: Store - ) {} -} diff --git a/projects/nshmp-apps/src/app/gmm/distance/state/app.reducer.ts b/projects/nshmp-apps/src/app/gmm/distance/state/app.reducer.ts deleted file mode 100644 index 78d59977a..000000000 --- a/projects/nshmp-apps/src/app/gmm/distance/state/app.reducer.ts +++ /dev/null @@ -1,281 +0,0 @@ -import {GmmFormControlIds, gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; -import {createFeature, createReducer, createSelector, on} from '@ngrx/store'; -import { - box, - createFormGroupState, - disable, - enable, - onNgrxForms, - onNgrxFormsAction, - setValue, - SetValueAction, - unbox, - updateGroup, - wrapReducerWithFormStateUpdate, -} from 'ngrx-forms'; -import {environment} from 'projects/nshmp-apps/src/environments/environment'; -import { - onPlotRedraw, - onPlotSettingsForm, - onResetSetting, -} from 'projects/nshmp-apps/src/shared/state/shared'; - -import {FormControls} from '../models/form-controls.model'; -import { - defaultFormValues, - defaultPlots, - MEAN_PLOT_SETTING_ID, -} from '../utils/app.default-values'; -import {validateControlPanelForm} from '../utils/control-form.validators'; -import {serviceResponseToPlotData} from '../utils/response.handler'; -import {actions} from './app.actions'; -import {AppState, CONTROL_FORM_ID, INITIAL_STATE} from './app.state'; - -/** Form state key ids */ -const formKeys = { - control: { - gmmSource: `${CONTROL_FORM_ID}.${GmmFormControlIds.GMM_SOURCE}`, - multiSelectable: `${CONTROL_FORM_ID}.${GmmFormControlIds.MULTI_SELECTABLE_PARAM}`, - Mw: `${CONTROL_FORM_ID}.${GmmFormControlIds.MW}`, - showEpistemicUncertainty: `${CONTROL_FORM_ID}.${GmmFormControlIds.SHOW_EPISTEMIC_UNCERTAINTY}`, - vs30: `${CONTROL_FORM_ID}.${GmmFormControlIds.VS30}`, - }, - settings: { - mean: MEAN_PLOT_SETTING_ID, - }, -}; - -const baseUrl = environment.webServices.data.url; -const endpoint = environment.webServices.data.services.gmmDistance; -const serviceUrl = `${baseUrl}${endpoint}`; - -/** - * GMM distance NGRX application feature. - */ -export const distanceAppFeature = createFeature({ - extraSelectors: ({selectPlots}) => ({ - selectMeanPlot: createSelector(selectPlots, plots => - plots.get(gmmUtils.PlotType.MEANS) - ), - }), - name: 'gmmDistanceApp', - reducer: createReducer( - /** Application intitial sate */ - INITIAL_STATE, - /* On NGRX forms */ - onNgrxForms(), - /* On form change */ - onNgrxFormsAction(SetValueAction, (state, action) => { - const clearPlots = new Map<string, NshmpPlot>(); - - state.plots.forEach((plot, key) => - clearPlots.set(key, { - ...plot, - plotData: { - ...plot.plotData, - data: [ - ...plot.plotData.data.map(data => { - data = {...data}; - data.x = []; - data.y = []; - return data; - }), - ], - }, - }) - ); - - const clearState: AppState = { - ...state, - controlForm: updateGroup<FormControls>({ - showEpistemicUncertainty: control => - disable(setValue(control, false)), - })(state.controlForm), - plots: clearPlots, - serviceResponses: null, - }; - - const serviceCallInfo = gmmUtils.serviceCallInfo({ - multiSelectableParam: clearState.controlForm.value.multiSelectableParam, - serviceName: clearState.serviceCallInfo.serviceName, - serviceResponses: clearState.serviceResponses, - serviceUrl, - values: clearState.controlForm.value, - }); - - switch (action.controlId) { - case formKeys.control.multiSelectable: { - let form = {...clearState.controlForm}; - const parameters = - clearState.usageResponses?.response?.parameters || null; - - if (form.value.multiSelectableParam === GmmFormControlIds.MW) { - form = updateGroup<FormControls>({ - gmmSource: gmm => setValue(gmm, box([])), - Mw: Mw => setValue(Mw, (parameters.Mw.value as number) || null), - MwMulti: Mw => setValue(Mw, box([])), - })(clearState.controlForm); - } else if ( - form.value.multiSelectableParam === GmmFormControlIds.VS30 - ) { - form = updateGroup<FormControls>({ - gmmSource: gmm => setValue(gmm, box([])), - vs30: vs30 => - setValue(vs30, (parameters.vs30.value as number) || null), - vs30Multi: vs30 => setValue(vs30, box([])), - })(clearState.controlForm); - } - - return { - ...clearState, - controlForm: form, - serviceCallInfo, - }; - } - case formKeys.control.gmmSource: { - const gmm = unbox(clearState.controlForm.value.gmmSource); - - if ( - clearState.usageResponses && - clearState.controlForm.controls.gmmSource && - gmm.length > 0 - ) { - const supportedImts = gmmUtils.getSupportedImts( - gmm, - clearState.usageResponses - ); - const controlForm = gmmUtils.onGmmChange( - clearState.controlForm, - supportedImts - ); - return { - ...clearState, - controlForm, - serviceCallInfo, - supportedImts, - }; - } - return { - ...clearState, - serviceCallInfo, - }; - } - case formKeys.control.showEpistemicUncertainty: { - return { - ...state, - plots: serviceResponseToPlotData(state), - }; - } - default: { - if (action.controlId.includes(formKeys.settings.mean)) { - return onPlotSettingsForm(state, action); - } else { - return { - ...clearState, - serviceCallInfo, - }; - } - } - } - }), - /* On init action */ - on(actions.init, () => { - return { - ...INITIAL_STATE, - }; - }), - /* On reset control panel action */ - on(actions.resetControlPanel, state => { - const controlForm = createFormGroupState<FormControls>( - CONTROL_FORM_ID, - defaultFormValues(state.usageResponses.response.parameters) - ); - - state = { - ...state, - controlForm, - plots: INITIAL_STATE.plots, - serviceResponses: INITIAL_STATE.serviceResponses, - }; - - return { - ...state, - serviceCallInfo: gmmUtils.serviceCallInfo({ - multiSelectableParam: state.controlForm.value.multiSelectableParam, - serviceName: state.serviceCallInfo.serviceName, - serviceResponses: state.serviceResponses, - serviceUrl, - values: state.controlForm.value, - }), - }; - }), - /* On reset plot settings action */ - on(actions.resetSettings, state => { - return onResetSetting(state, defaultPlots()); - }), - /* On plots action */ - on(actions.plots, (state, {plots}) => { - return { - ...state, - plots, - }; - }), - /* On plot redraw action */ - on(actions.plotRedraw, state => { - return onPlotRedraw(state); - }), - /* On service call info action */ - on(actions.serviceCallInfo, (state, {serviceCallInfo}) => { - return { - ...state, - serviceCallInfo: { - ...state.serviceCallInfo, - ...serviceCallInfo, - }, - }; - }), - /* On service response action */ - on(actions.serviceResponse, (state, {serviceResponses}) => { - const response = serviceResponses; - const means = response.map(s => s.response.means); - const sigmas = response.map(s => s.response.sigmas); - const hasLogicTree = gmmUtils.hasTree([...means, ...sigmas]); - - state = { - ...state, - controlForm: updateGroup<FormControls>({ - showEpistemicUncertainty: hasLogicTree ? enable : disable, - })(state.controlForm), - serviceResponses: response, - }; - - return { - ...state, - serviceCallInfo: gmmUtils.serviceCallInfo({ - multiSelectableParam: state.controlForm.value.multiSelectableParam, - serviceName: state.serviceCallInfo.serviceName, - serviceResponses: state.serviceResponses, - serviceUrl, - values: state.controlForm.value, - }), - }; - }), - /* On usage response action */ - on(actions.usageResponse, (state, {usageResponses}) => { - return { - ...state, - usageResponses: usageResponses, - }; - }) - ), -}); - -/** - * Application NGRX reducer with validators. - */ -distanceAppFeature.reducer = wrapReducerWithFormStateUpdate( - distanceAppFeature.reducer, - state => state.controlForm, - validateControlPanelForm -); diff --git a/projects/nshmp-apps/src/app/gmm/gmm.routes.ts b/projects/nshmp-apps/src/app/gmm/gmm.routes.ts index 15ed49622..52138a14c 100644 --- a/projects/nshmp-apps/src/app/gmm/gmm.routes.ts +++ b/projects/nshmp-apps/src/app/gmm/gmm.routes.ts @@ -2,8 +2,6 @@ import {Routes} from '@angular/router'; import {provideEffects} from '@ngrx/effects'; import {provideState} from '@ngrx/store'; -import {GmmDistanceAppEffects} from './distance/state/app.effects'; -import {distanceAppFeature} from './distance/state/app.reducer'; import {MagnitudeAppEffects} from './magnitude/state/app.effects'; import {magnitudeAppFeature} from './magnitude/state/app.reducer'; import {SpectraAppEffects} from './spectra/state/app.effects'; @@ -15,10 +13,6 @@ const routes: Routes = [ loadComponent: () => import('./distance/app.component').then(com => com.AppComponent), path: 'distance', - providers: [ - provideState(distanceAppFeature), - provideEffects(GmmDistanceAppEffects), - ], }, { loadComponent: () => -- GitLab From c0324ee1f8f8ebb396e8b18b933215b75403d069 Mon Sep 17 00:00:00 2001 From: Brandon Clayton <bclayton@usgs.gov> Date: Thu, 25 Jul 2024 15:55:53 -0600 Subject: [PATCH 2/4] switch to reactive forms and signals --- .../components/content/content.component.html | 8 +- .../components/content/content.component.ts | 21 +- .../control-panel.component.html | 419 +++++++++--------- .../control-panel/control-panel.component.ts | 154 +++++-- .../parameter-summary.component.html | 154 ++++--- .../parameter-summary.component.ts | 14 +- .../components/plots/plots.component.html | 10 +- .../components/plots/plots.component.ts | 15 +- .../settings/settings.component.html | 12 +- .../components/settings/settings.component.ts | 21 +- .../distance/models/form-controls.model.ts | 5 +- .../src/app/gmm/distance/state/app.facade.ts | 229 ++++++++-- .../src/app/gmm/distance/state/app.state.ts | 39 +- .../gmm/distance/utils/app.default-values.ts | 24 +- .../gmm/distance/utils/response.handler.ts | 26 +- 15 files changed, 697 insertions(+), 454 deletions(-) diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.html index a9334d5c7..9d1df4a31 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.html +++ b/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.html @@ -10,15 +10,15 @@ <mat-tab labelClass="medians-tab" label="Medians" - [disabled]="(hasData$ | async) === false" + [disabled]="hasData() === false" > <ng-template matTabContent> - <nshmp-lib-ng-export-data-table - [table]="table$ | async" + <nshmp-lib-no-ngrx-export-data-table + [table]="table()" filename="gmm-distance-means.csv" buttonText="Export Means to CSV" /> - <nshmp-lib-ng-data-table [table]="table$ | async" /> + <nshmp-lib-no-ngrx-data-table [table]="table()" /> </ng-template> </mat-tab> </mat-tab-group> diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.ts b/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.ts index 11efd9b21..03e93dac3 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.ts @@ -1,12 +1,10 @@ -import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import {MatTab, MatTabContent, MatTabGroup} from '@angular/material/tabs'; -import {gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import { NshmpLibNgDataTableComponent, NshmpLibNgExportDataTableComponent, -} from '@ghsc/nshmp-lib-ng/nshmp'; -import {map} from 'rxjs/operators'; +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import {GmmDistanceAppFacade} from '../../state/app.facade'; import {PlotsComponent} from '../plots/plots.component'; @@ -22,7 +20,6 @@ import {PlotsComponent} from '../plots/plots.component'; MatTab, MatTabContent, PlotsComponent, - AsyncPipe, NshmpLibNgExportDataTableComponent, NshmpLibNgDataTableComponent, ], @@ -38,14 +35,14 @@ export class ContentComponent { exp = true; /** Whether service has been called and data exists */ - hasData$ = this.facade.serviceResponse$.pipe( - map(responseSpectra => responseSpectra?.length > 0) - ); + hasData = computed(() => this.facade.serviceResponse()?.length > 0); /** Table data for table */ - table$ = this.facade.meanPlotState$.pipe( - map(plot => - gmmUtils.plotToTable(plot.plotData, this.exp, this.commonXValues) + table = computed(() => + gmmUtils.plotToTable( + this.facade.meanPlotState().plotData, + this.exp, + this.commonXValues ) ); diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.html index 55bdb4404..55b262f71 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.html +++ b/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.html @@ -1,221 +1,216 @@ <!-- GMM Distance Conrol Panel --> -@if (form$ | async) { - <form - class="settings-section control-panel height-full overflow-auto" - [ngrxFormState]="form$ | async" - (submit)="onSubmit()" - > - <mat-form-field - class="grid-col-12 multi-select-parameter-menu padding-top-1" - > - <mat-label>Multi-Selectable Parameter</mat-label> - <mat-select - [ngrxFormControlState]="(controls$ | async)?.multiSelectableParam" - > - <mat-option value="gmm">Ground Motion Models</mat-option> - <mat-option value="Mw">M<sub>W</sub></mat-option> - <mat-option value="vs30">V<sub>S30</sub></mat-option> - </mat-select> - </mat-form-field> - - <!-- GMM menu --> - <nshmp-lib-ng-gmm-menu - [gmmControlState]="(controls$ | async)?.gmmSource" - [gmmGroupTypeState]="(controls$ | async)?.gmmGroupType" - [multiple]="(controls$ | async)?.multiSelectableParam.value === 'gmm'" - [parameters]="parameters$ | async" +<form + class="settings-section control-panel height-full overflow-auto" + [formGroup]="form" + (submit)="onSubmit()" +> + <mat-form-field class="grid-col-12 multi-select-parameter-menu padding-top-1"> + <mat-label>Multi-Selectable Parameter</mat-label> + <mat-select [formControl]="controls.multiSelectableParam"> + <mat-option value="gmm">Ground Motion Models</mat-option> + <mat-option value="Mw">M<sub>W</sub></mat-option> + <mat-option value="vs30">V<sub>S30</sub></mat-option> + </mat-select> + </mat-form-field> + + <!-- GMM menu --> + @if (parameters()) { + <nshmp-lib-no-ngrx-gmm-menu + [gmmControl]="controls.gmmSource" + [gmmGroupTypeControl]="controls.gmmGroupType" + [multiple]="controls.multiSelectableParam.value === 'gmm'" + [parameters]="parameters()" + [imtControl]="controls.imt" + [supportedImts]="supportedImts()" /> - - <!-- IMT select menu --> - <mat-form-field class="grid-col-12 imt-select"> - <mat-label>Intensity Measure Type</mat-label> - <mat-select - #imtSelectEl - [ngrxFormControlState]="(controls$ | async)?.imt" - > - @if ((gmmSelected$ | async) === false) { - <mat-option selected="true" value="default"> - --- Choose a GMM --- - </mat-option> - } - @for (imt of supportedImts$ | async; track imt) { - <mat-option [value]="imt.value"> - {{ imt.display }} - </mat-option> - } - </mat-select> - </mat-form-field> - - <!-- Event parameters --> - <div class="settings-subsection event-parameters"> - <mat-label class="settings-subsection--label">Event Parameters</mat-label> - - <!-- Event parameters: Mw input --> - <div class="settings-subsection--section"> - <nshmp-lib-ng-gmm-multi-select - class="grid-col-6 mw-input" - label="Mw" - [multiple]="MwMultiple$ | async" - [numberControl]="(controls$ | async)?.Mw" - [parameter]="(parameters$ | async)?.Mw" - placeholder="Select Magnitudes" - [selectControl]="(controls$ | async)?.MwMulti" - [selectOptions]="MwCommonValues" - /> - </div> + } + + <!-- IMT select menu --> + <mat-form-field class="grid-col-12 imt-select"> + <mat-label>Intensity Measure Type</mat-label> + <mat-select [formControl]="controls?.imt"> + @if (gmmSelected === false) { + <mat-option selected="true" value="default"> + --- Choose a GMM --- + </mat-option> + } + @for (imt of supportedImts(); track imt) { + <mat-option [value]="imt.value"> + {{ imt.display }} + </mat-option> + } + </mat-select> + </mat-form-field> + + <!-- Event parameters --> + <div class="settings-subsection event-parameters"> + <mat-label class="settings-subsection--label">Event Parameters</mat-label> + + <!-- Event parameters: Mw input --> + <div class="settings-subsection--section"> + <nshmp-lib-no-ngrx-gmm-multi-select + class="grid-col-6 mw-input" + label="Mw" + [multiple]="MwMultiple$ | async" + [numberControl]="controls.Mw" + [parameter]="parameters()?.Mw" + placeholder="Select Magnitudes" + [selectControl]="controls?.MwMulti" + [selectOptions]="MwCommonValues" + /> </div> - - <!-- Source geometry --> - <div class="settings-subsection geometry-parameters"> - <mat-label class="settings-subsection--label">Source Geometry</mat-label> - - <div class="settings-subsection--section grid-row grid-gap-sm"> - <!-- Source geometry: zTor input --> - <mat-form-field class="grid-col-4 ztor-input"> - <mat-label>Z<sub>TOR</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.zTor.max" - [min]="(parameters$ | async)?.zTor.min" - [ngrxFormControlState]="(controls$ | async)?.zTor" - step="0.5" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.zTor.min }}, - {{ (parameters$ | async)?.zTor.max }} - ] - </mat-error> - </mat-form-field> - - <!-- Source geometry: Dip input --> - <mat-form-field class="grid-col-4 dip-input"> - <mat-label>Dip (°)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.dip.max" - [min]="(parameters$ | async)?.dip.min" - [ngrxFormControlState]="(controls$ | async)?.dip" - step="0.5" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.dip.min }}, - {{ (parameters$ | async)?.dip.max }} - ] - </mat-error> - </mat-form-field> - - <!-- Source geometry: Width input --> - <mat-form-field class="grid-col-4 width-input"> - <mat-label>Width (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.width.max" - [min]="(parameters$ | async)?.width.min" - [ngrxFormControlState]="(controls$ | async)?.width" - step="0.5" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.width.min }}, - {{ (parameters$ | async)?.width.max }} - ] - </mat-error> - </mat-form-field> - </div> + </div> + + <!-- Source geometry --> + <div class="settings-subsection geometry-parameters"> + <mat-label class="settings-subsection--label">Source Geometry</mat-label> + + <div class="settings-subsection--section grid-row grid-gap-sm"> + <!-- Source geometry: zTor input --> + <mat-form-field class="grid-col-4 ztor-input"> + <mat-label>Z<sub>TOR</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.zTor.max" + [min]="parameters()?.zTor.min" + [formControl]="controls.zTor" + step="0.5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.zTor.min }}, + {{ parameters()?.zTor.max }} + ] + </mat-error> + </mat-form-field> + + <!-- Source geometry: Dip input --> + <mat-form-field class="grid-col-4 dip-input"> + <mat-label>Dip (°)</mat-label> + <input + matInput + [max]="parameters()?.dip.max" + [min]="parameters()?.dip.min" + [formControl]="controls?.dip" + step="0.5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.dip.min }}, + {{ parameters()?.dip.max }} + ] + </mat-error> + </mat-form-field> + + <!-- Source geometry: Width input --> + <mat-form-field class="grid-col-4 width-input"> + <mat-label>Width (km)</mat-label> + <input + matInput + [max]="parameters()?.width.max" + [min]="parameters()?.width.min" + [formControl]="controls.width" + step="0.5" + type="number" + /> + <mat-error> + [ + {{ parameters()?.width.min }}, + {{ parameters()?.width.max }} + ] + </mat-error> + </mat-form-field> </div> - - <!-- Site & Basin--> - <div class="settings-subsection site-parameters"> - <mat-label class="settings-subsection--label">Site & Basin</mat-label> - - <div class="settings-subsection--section grid-row grid-gap-sm"> - <!-- Site & Basin: Vs30 input --> - <nshmp-lib-ng-gmm-multi-select - class="grid-col-12 vs30-input" - label="V<sub>s30</sub> (<sup>m</sup>/<sub>s</sub>)" - [multiple]="vs30Multiple$ | async" - [numberControl]="(controls$ | async)?.vs30" - [parameter]="(parameters$ | async)?.vs30" - placeholder="Select values..." - [selectControl]="(controls$ | async)?.vs30Multi" - [selectOptions]="vs30CommonValues" + </div> + + <!-- Site & Basin--> + <div class="settings-subsection site-parameters"> + <mat-label class="settings-subsection--label">Site & Basin</mat-label> + + <div class="settings-subsection--section grid-row grid-gap-sm"> + <!-- Site & Basin: Vs30 input --> + <nshmp-lib-no-ngrx-gmm-multi-select + class="grid-col-12 vs30-input" + label="V<sub>s30</sub> (<sup>m</sup>/<sub>s</sub>)" + [multiple]="vs30Multiple$ | async" + [numberControl]="controls.vs30" + [parameter]="parameters()?.vs30" + placeholder="Select values..." + [selectControl]="controls?.vs30Multi" + [selectOptions]="vs30CommonValues" + /> + + <!-- Site & Basin: Z 1.0 input --> + <mat-form-field class="grid-col-4 z1p0-input"> + <mat-label>Z<sub>1.0</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.z1p0.max" + [min]="parameters()?.z1p0.min" + [formControl]="controls.z1p0" + step="1.0" + type="number" /> - - <!-- Site & Basin: Z 1.0 input --> - <mat-form-field class="grid-col-4 z1p0-input"> - <mat-label>Z<sub>1.0</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.z1p0.max" - [min]="(parameters$ | async)?.z1p0.min" - [ngrxFormControlState]="(controls$ | async)?.z1p0" - step="1.0" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.z1p0.min }}, - {{ (parameters$ | async)?.z1p0.max }} - ] - </mat-error> - </mat-form-field> - - <!-- Site & Basin: Z 2.5 input --> - <mat-form-field class="grid-col-4 z2p5-input"> - <mat-label>Z<sub>2.5</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.z2p5.max" - [min]="(parameters$ | async)?.z2p5.min" - [ngrxFormControlState]="(controls$ | async)?.z2p5" - step="1.0" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.z2p5.min }}, - {{ (parameters$ | async)?.z2p5.max }} - ] - </mat-error> - </mat-form-field> - - <!-- Site & Basin: zSed input --> - <mat-form-field class="grid-col-4 zsed-input"> - <mat-label>Z<sub>SED</sub> (km)</mat-label> - <input - matInput - [max]="(parameters$ | async)?.zSed?.max" - [min]="(parameters$ | async)?.zSed?.min" - [ngrxFormControlState]="(controls$ | async)?.zSed" - step="1.0" - type="number" - /> - <mat-error> - [ - {{ (parameters$ | async)?.zSed?.min }}, - {{ (parameters$ | async)?.zSed?.max }} - ] - </mat-error> - </mat-form-field> - </div> + <mat-error> + [ + {{ parameters()?.z1p0.min }}, + {{ parameters()?.z1p0.max }} + ] + </mat-error> + </mat-form-field> + + <!-- Site & Basin: Z 2.5 input --> + <mat-form-field class="grid-col-4 z2p5-input"> + <mat-label>Z<sub>2.5</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.z2p5.max" + [min]="parameters()?.z2p5.min" + [formControl]="controls.z2p5" + step="1.0" + type="number" + /> + <mat-error> + [ + {{ parameters()?.z2p5.min }}, + {{ parameters()?.z2p5.max }} + ] + </mat-error> + </mat-form-field> + + <!-- Site & Basin: zSed input --> + <mat-form-field class="grid-col-4 zsed-input"> + <mat-label>Z<sub>SED</sub> (km)</mat-label> + <input + matInput + [max]="parameters()?.zSed?.max" + [min]="parameters()?.zSed?.min" + [formControl]="controls.zSed" + step="1.0" + type="number" + /> + <mat-error> + [ + {{ parameters()?.zSed?.min }}, + {{ parameters()?.zSed?.max }} + ] + </mat-error> + </mat-form-field> </div> + </div> - <nshmp-lib-ng-gmm-plot-options-control-panel - [showEpistemicFormControl]="(controls$ | async)?.showEpistemicUncertainty" - /> + <nshmp-lib-no-ngrx-gmm-plot-options-control-panel + [showEpistemicFormControl]="controls?.showEpistemicUncertainty" + /> - <div class="padding-y-3"></div> + <div class="padding-y-3"></div> - <nshmp-lib-ng-control-panel-buttons - [plotDisabled]="(form$ | async)?.isInvalid" - [serviceCallInfo]="serviceCallInfo$ | async" - [resetDisabled]="(form$ | async)?.isPristine" - (resetClick)="facade.resetControlPanel()" - /> - </form> -} + <nshmp-lib-no-ngrx-control-panel-buttons + [plotDisabled]="form.invalid" + [serviceCallInfo]="serviceCallInfo()" + [resetDisabled]="form.pristine" + (resetClick)="facade.resetControlPanel()" + /> +</form> diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.ts b/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.ts index c52863ee4..e4718bf16 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.ts @@ -1,23 +1,24 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed, OnDestroy, OnInit} from '@angular/core'; +import {ReactiveFormsModule} from '@angular/forms'; import {MatOption} from '@angular/material/core'; import {MatError, MatFormField, MatLabel} from '@angular/material/form-field'; import {MatInput} from '@angular/material/input'; import {MatSelect} from '@angular/material/select'; import { GmmFormControlIds, + gmmUtils, MW_COMMON_VALUES, NshmpLibNgGmmMenuComponent, NshmpLibNgGmmMultiSelectComponent, NshmpLibNgGmmPlotOptionsControlPanelComponent, VS30_COMMON_VALUES, -} from '@ghsc/nshmp-lib-ng/gmm'; +} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import { NshmpLibNgControlPanelButtonsComponent, - NshmpNgrxFormsModule, NshmpService, -} from '@ghsc/nshmp-lib-ng/nshmp'; -import {unbox} from 'ngrx-forms'; +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {combineLatest, Subscription} from 'rxjs'; import {map} from 'rxjs/operators'; import {GmmDistanceAppFacade} from '../../state/app.facade'; @@ -38,69 +39,104 @@ import {GmmDistanceAppFacade} from '../../state/app.facade'; NshmpLibNgGmmMultiSelectComponent, NshmpLibNgGmmPlotOptionsControlPanelComponent, NshmpLibNgControlPanelButtonsComponent, - NshmpNgrxFormsModule, + ReactiveFormsModule, ], selector: 'app-control-panel', standalone: true, styleUrl: './control-panel.component.scss', templateUrl: './control-panel.component.html', }) -export class ControlPanelComponent { +export class ControlPanelComponent implements OnInit, OnDestroy { /** List of common magnitudes */ MwCommonValues = MW_COMMON_VALUES; /** List of common vs30 values */ vs30CommonValues = VS30_COMMON_VALUES; /** Form controls state */ - controls$ = this.facade.controlForm$.pipe(map(form => form.controls)); + controls = this.facade.formGroup.controls; /** Control panel form field state */ - form$ = this.facade.controlForm$; + form = this.facade.formGroup; /** Whether a GMM has been selected */ - gmmSelected$ = this.controls$.pipe( - map(controls => unbox(controls.gmmSource.value).length > 0) - ); - - /** List magnitudes */ - MwList$ = this.controls$.pipe(map(controls => unbox(controls.Mw.value))); + gmmSelected = this.controls.gmmSource.getRawValue()?.length > 0; /** Usage parameters */ - parameters$ = this.facade.usage$.pipe( - map(usage => usage?.response?.parameters) - ); + parameters = computed(() => this.facade.usage()?.response.parameters); /** Service call info state */ - serviceCallInfo$ = this.facade.serviceCallInfo$; + serviceCallInfo = this.facade.serviceCallInfo; /** Supported IMTs based on GMMs selected */ - supportedImts$ = this.facade.supportedImts$; + supportedImts = this.facade.supportedImts; /** Whether magnitudes are multi-selectable */ - MwMultiple$ = this.controls$.pipe( + MwMultiple$ = this.controls.multiSelectableParam.valueChanges.pipe( map( - controls => - controls.multiSelectableParam.value === GmmFormControlIds.MW.toString() + multiSelectableParam => + multiSelectableParam.toString() === GmmFormControlIds.MW.toString() ) ); - /** List of Vs30s */ - vs30List$ = this.controls$.pipe(map(controls => unbox(controls.vs30.value))); - /** Whether Vs30 is multi-selectable */ - vs30Multiple$ = this.controls$.pipe( + vs30Multiple$ = this.controls.multiSelectableParam.valueChanges.pipe( map( - controls => - controls.multiSelectableParam.value === - GmmFormControlIds.VS30.toString() + multiSelectableParam => + multiSelectableParam.toString() === GmmFormControlIds.VS30.toString() ) ); + private subs: Subscription[] = []; + constructor( public facade: GmmDistanceAppFacade, private nshmpService: NshmpService ) {} + ngOnInit(): void { + this.subs.push( + this.controls.gmmSource.valueChanges.subscribe(() => { + this.onGmmSource(); + }) + ); + + this.subs.push( + this.controls.multiSelectableParam.valueChanges.subscribe(() => + this.onMultiSelectableParam() + ) + ); + + this.subs.push( + this.controls.showEpistemicUncertainty.valueChanges.subscribe(() => + this.onShowEpistemicUncertainty() + ) + ); + + this.subs.push( + combineLatest([ + this.controls.dip.valueChanges, + this.controls.width.valueChanges, + ]).subscribe(() => this.updatePlots()) + ); + + this.subs.push( + combineLatest([ + this.controls.imt.valueChanges, + this.controls.Mw.valueChanges, + this.controls.MwMulti.valueChanges, + this.controls.vs30Multi.valueChanges, + this.controls.vs30.valueChanges, + this.controls.z1p0.valueChanges, + this.controls.z2p5.valueChanges, + this.controls.zSed.valueChanges, + ]).subscribe(() => this.facade.resetState()) + ); + } + + ngOnDestroy(): void { + this.subs.forEach(sub => sub?.unsubscribe()); + } + /** * On form submit. */ @@ -108,4 +144,62 @@ export class ControlPanelComponent { this.facade.callService(); this.nshmpService.selectPlotControl(); } + + private onGmmSource() { + const formValues = this.form.getRawValue(); + + if ( + !this.facade.state().usageResponse || + formValues.gmmSource.length === 0 + ) { + this.controls.imt.patchValue(gmmUtils.imtPlaceHolder().value); + this.facade.updateState({ + supportedImts: [], + }); + return; + } + + const supportedImts = gmmUtils.getSupportedImts( + formValues.gmmSource, + this.facade.state().usageResponse + ); + + this.facade.updateState({ + supportedImts, + }); + + const imt = [...supportedImts].shift(); + + this.controls.imt.patchValue(imt?.value); + this.facade.resetState(); + } + + private onMultiSelectableParam(): void { + if (!this.facade.state().usageResponse) { + return; + } + + const {multiSelectableParam} = this.form.getRawValue(); + const parameters = this.facade.state().usageResponse.response.parameters; + + if (multiSelectableParam === GmmFormControlIds.MW) { + this.controls.gmmSource.setValue([]); + this.controls.Mw.setValue(parameters.Mw.value as number); + this.controls.MwMulti.setValue([]); + } else if (multiSelectableParam === GmmFormControlIds.VS30) { + this.controls.gmmSource.setValue([]); + this.controls.vs30.setValue(parameters.vs30.value as number); + this.controls.vs30Multi.setValue([]); + } + + this.facade.resetState(); + } + + private onShowEpistemicUncertainty(): void { + this.facade.createPlots(); + } + + private updatePlots(): void { + this.facade.createPlots(); + } } diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.html index 68392622e..d61b7f901 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.html +++ b/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.html @@ -1,98 +1,96 @@ -@if (form$ | async; as form) { - <div class="grid-row parameter-summary"> - <div class="grid-col-12 tablet-lg:grid-col-6"> - <mat-list class="parameter-list"> - <mat-list-item> - <span class="parameter">Ground Motion Models</span>: +<div class="grid-row parameter-summary"> + <div class="grid-col-12 tablet-lg:grid-col-6"> + <mat-list class="parameter-list"> + <mat-list-item> + <span class="parameter">Ground Motion Models</span>: + </mat-list-item> + + @for (gmmSource of form.value.gmmSource; track gmmSource) { + <mat-list-item class="indent-list font-small"> + {{ gmmSource.gmm.display }} </mat-list-item> + } + </mat-list> + </div> - @for (gmmSource of unbox(form?.value?.gmmSource); track gmmSource) { - <mat-list-item class="indent-list font-small"> - {{ gmmSource.gmm.display }} - </mat-list-item> - } - </mat-list> - </div> + <div class="grid-col-12 tablet-lg:grid-col-3"> + <mat-list class="parameter-list"> + <mat-list-item> + <span class="parameter">IMT</span>: + {{ imtIdToDisplay(form?.value?.imt) }} + </mat-list-item> - <div class="grid-col-12 tablet-lg:grid-col-3"> - <mat-list class="parameter-list"> + @if (form.value.multiSelectableParam === GmmFormControlIds.MW) { <mat-list-item> - <span class="parameter">IMT</span>: - {{ imtIdToDisplay(form?.value?.imt) }} + <span class="parameter">Magnitude</span>: </mat-list-item> - - @if (form.value.multiSelectableParam === GmmFormControlIds.MW) { - <mat-list-item> - <span class="parameter">Magnitude</span>: - </mat-list-item> - @for (Mw of unbox(form?.value?.MwMulti); track Mw) { - <mat-list-item class="indent-list"> - {{ Mw }} - </mat-list-item> - } - } @else { - <mat-list-item> - <span class="parameter">Magnitude</span>: - {{ form?.value?.Mw }} + @for (Mw of form?.value?.MwMulti; track Mw) { + <mat-list-item class="indent-list"> + {{ Mw }} </mat-list-item> } - + } @else { <mat-list-item> - <span class="parameter"> Z<sub>TOR</sub> </span>: - {{ form?.value?.zTor }} km + <span class="parameter">Magnitude</span>: + {{ form?.value?.Mw }} </mat-list-item> + } - <mat-list-item> - <span class="parameter">Dip</span>: {{ form?.value?.dip }}° - </mat-list-item> + <mat-list-item> + <span class="parameter"> Z<sub>TOR</sub> </span>: + {{ form?.value?.zTor }} km + </mat-list-item> - <mat-list-item> - <span class="parameter">Width</span>: {{ form?.value?.width }} km - </mat-list-item> - </mat-list> - </div> + <mat-list-item> + <span class="parameter">Dip</span>: {{ form?.value?.dip }}° + </mat-list-item> - <div class="grid-col-12 tablet-lg:grid-col-3"> - <mat-list class="parameter-list"> - @if (form.value.multiSelectableParam === GmmFormControlIds.VS30) { - <mat-list-item> <span class="parameter">Vs30</span>: </mat-list-item> - @for (vs30 of unbox(form?.value?.vs30Multi); track vs30) { - <mat-list-item class="indent-list"> - {{ vs30 }} - <sup> m</sup>/<sub>s</sub> - </mat-list-item> - } - } @else { - <mat-list-item> - <span class="parameter">Vs30</span>: {{ form?.value?.vs30 }} + <mat-list-item> + <span class="parameter">Width</span>: {{ form?.value?.width }} km + </mat-list-item> + </mat-list> + </div> + + <div class="grid-col-12 tablet-lg:grid-col-3"> + <mat-list class="parameter-list"> + @if (form.value.multiSelectableParam === GmmFormControlIds.VS30) { + <mat-list-item> <span class="parameter">Vs30</span>: </mat-list-item> + @for (vs30 of form?.value?.vs30Multi; track vs30) { + <mat-list-item class="indent-list"> + {{ vs30 }} <sup> m</sup>/<sub>s</sub> </mat-list-item> } - + } @else { <mat-list-item> - <span class="parameter"> Z<sub>1.0</sub> </span>: - {{ form?.value?.z1p0 }} - @if (form?.value?.z1p0) { - <span> km</span> - } + <span class="parameter">Vs30</span>: {{ form?.value?.vs30 }} + <sup> m</sup>/<sub>s</sub> </mat-list-item> + } - <mat-list-item> - <span class="parameter"> Z<sub>2.5</sub> </span>: - {{ form?.value?.z2p5 }} - @if (form?.value?.z2p5) { - <span> km</span> - } - </mat-list-item> + <mat-list-item> + <span class="parameter"> Z<sub>1.0</sub> </span>: + {{ form?.value?.z1p0 }} + @if (form?.value?.z1p0) { + <span> km</span> + } + </mat-list-item> - <mat-list-item> - <span class="parameter"> Z<sub>SED</sub> </span>: - {{ form?.value?.zSed }} - @if (form?.value?.zSed) { - <span> km</span> - } - </mat-list-item> - </mat-list> - </div> + <mat-list-item> + <span class="parameter"> Z<sub>2.5</sub> </span>: + {{ form?.value?.z2p5 }} + @if (form?.value?.z2p5) { + <span> km</span> + } + </mat-list-item> + + <mat-list-item> + <span class="parameter"> Z<sub>SED</sub> </span>: + {{ form?.value?.zSed }} + @if (form?.value?.zSed) { + <span> km</span> + } + </mat-list-item> + </mat-list> </div> -} +</div> diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.ts b/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.ts index 2914b9a18..1881477ff 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.ts @@ -1,9 +1,7 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; import {MatList, MatListItem} from '@angular/material/list'; -import {GmmFormControlIds, gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; -import {unbox} from 'ngrx-forms'; -import {map} from 'rxjs'; +import {GmmFormControlIds, gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import {GmmDistanceAppFacade} from '../../state/app.facade'; @@ -23,16 +21,12 @@ export class ParameterSummaryComponent { /** Function to convert IMT id to display */ imtIdToDisplay = gmmUtils.imtIdToDisplay; - /** ngrx-forms unbox function */ - unbox = unbox; /** Control form field state */ - form$ = this.facade.controlForm$; + form = this.facade.formGroup; /** List of GMMs */ - gmms$ = this.facade.usage$.pipe( - map(usage => usage?.response?.parameters?.gmm.values) - ); + gmms = computed(() => this.facade.usage()?.response.parameters.gmm.values); constructor(private facade: GmmDistanceAppFacade) {} } diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.html index 0365bce50..38224cae3 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.html +++ b/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.html @@ -1,5 +1,5 @@ <!-- Means --> -<nshmp-lib-ng-plots-container> +<nshmp-lib-no-ngrx-plots-container> <mat-accordion multi> <!-- GMM vs. distance plot --> <mat-expansion-panel expanded> @@ -9,8 +9,8 @@ <mat-divider /> - @if (meanPlotData$ | async; as meanPlot) { - <nshmp-lib-ng-plot class="distance-plot" [plot]="meanPlot" /> + @if (meanPlotData()) { + <nshmp-lib-no-ngrx-plot class="distance-plot" [plot]="meanPlotData()" /> } </mat-expansion-panel> @@ -31,7 +31,7 @@ </mat-expansion-panel-header> <mat-divider /> - <nshmp-lib-ng-app-metadata [repositories]="repositories$ | async" /> + <nshmp-lib-no-ngrx-app-metadata [repositories]="repositories()" /> </mat-expansion-panel> </mat-accordion> -</nshmp-lib-ng-plots-container> +</nshmp-lib-no-ngrx-plots-container> diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.ts b/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.ts index 9e7f3561a..fdbf522bf 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.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, @@ -7,12 +7,11 @@ import { MatExpansionPanelHeader, MatExpansionPanelTitle, } from '@angular/material/expansion'; -import {NshmpLibNgAppMetadataComponent} from '@ghsc/nshmp-lib-ng/nshmp'; +import {NshmpLibNgAppMetadataComponent} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; import { NshmpLibNgPlotComponent, NshmpLibNgPlotsContainerComponent, -} from '@ghsc/nshmp-lib-ng/plot'; -import {map} from 'rxjs/operators'; +} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {GmmDistanceAppFacade} from '../../state/app.facade'; import {ParameterSummaryComponent} from '../parameter-summary/parameter-summary.component'; @@ -40,12 +39,10 @@ import {ParameterSummaryComponent} from '../parameter-summary/parameter-summary. }) export class PlotsComponent { /** GM vs distance plot data */ - meanPlotData$ = this.facade.meanPlotState$.pipe(map(plot => plot.plotData)); + meanPlotData = computed(() => this.facade.meanPlotState()?.plotData); /** Repo metadata */ - repositories$ = this.facade.usage$.pipe( - map(usage => usage?.metadata.repositories) - ); + repositories = computed(() => this.facade.usage()?.metadata.repositories); - constructor(private facade: GmmDistanceAppFacade) {} + constructor(public facade: GmmDistanceAppFacade) {} } diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.html b/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.html index 78c062689..ef2792bbd 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.html +++ b/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.html @@ -1,18 +1,18 @@ <div class="height-full overflow-auto"> - @if (meanPlot$ | async) { - <nshmp-lib-ng-plot-settings + @if (meanPlot()) { + <nshmp-lib-no-ngrx-plot-settings class="distance-settings" - [plot]="meanPlot$ | async" + [plot]="meanPlot()" + (updatedPlot)="updatePlot($event)" /> } <div class="padding-y-3"></div> - <nshmp-lib-ng-plot-reset-plot-settings + <nshmp-lib-no-ngrx-plot-reset-plot-settings (resetClick)="facade.resetPlotSettings()" [resetDisabled]=" - (meanPlotSettings$ | async)?.isPristine && - (meanPlotSettings$ | async)?.isUntouched + meanPlotSettings()?.pristine && meanPlotSettings()?.untouched " /> </div> diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.ts b/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.ts index 526f961e9..0cc061dc6 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.ts @@ -1,10 +1,11 @@ import {AsyncPipe} from '@angular/common'; -import {Component} from '@angular/core'; +import {Component, computed} from '@angular/core'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; import { NshmpLibNgPlotResetPlotSettingsComponent, NshmpLibNgPlotSettingsComponent, -} from '@ghsc/nshmp-lib-ng/plot'; -import {map} from 'rxjs/operators'; + NshmpPlot, +} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {GmmDistanceAppFacade} from '../../state/app.facade'; @@ -24,12 +25,18 @@ import {GmmDistanceAppFacade} from '../../state/app.facade'; }) export class SettingsComponent { /** Plot data */ - meanPlot$ = this.facade.meanPlotState$; + meanPlot = this.facade.meanPlotState; /** Plot settings */ - meanPlotSettings$ = this.facade.meanPlotState$.pipe( - map(plot => plot.settingsForm) - ); + meanPlotSettings = computed(() => this.facade.meanPlotState()?.settingsForm); constructor(public facade: GmmDistanceAppFacade) {} + + updatePlot(plot: NshmpPlot): void { + const plots = new Map(this.facade.state().plots); + plots.set(gmmUtils.PlotType.MEANS, plot); + this.facade.updateState({ + plots, + }); + } } diff --git a/projects/nshmp-apps/src/app/gmm/distance/models/form-controls.model.ts b/projects/nshmp-apps/src/app/gmm/distance/models/form-controls.model.ts index c6831aaeb..ca7134ec9 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/models/form-controls.model.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/models/form-controls.model.ts @@ -1,4 +1,7 @@ -import {GmmFormControlIds, GmmImtFormControls} from '@ghsc/nshmp-lib-ng/gmm'; +import { + GmmFormControlIds, + GmmImtFormControls, +} from '@ghsc/nshmp-lib-no-ngrx/gmm'; /** * Control panel form fields. diff --git a/projects/nshmp-apps/src/app/gmm/distance/state/app.facade.ts b/projects/nshmp-apps/src/app/gmm/distance/state/app.facade.ts index 2ba9dcc30..0a17e865d 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/state/app.facade.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/state/app.facade.ts @@ -1,18 +1,33 @@ -import {Injectable} from '@angular/core'; -import {ServiceCallInfo} from '@ghsc/nshmp-lib-ng/nshmp'; -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; +import {computed, Injectable, Signal, signal} from '@angular/core'; +import {AbstractControl, FormBuilder, Validators} from '@angular/forms'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; +import { + FormGroupControls, + NshmpService, + ServiceCallInfo, + SpinnerService, +} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; import { GmmDistanceResponse, GmmDistanceUsage, } from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; import {EnumParameterValues} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; -import {select, Store} from '@ngrx/store'; -import {FormGroupState} from 'ngrx-forms'; -import {Observable} from 'rxjs'; +import {environment} from 'projects/nshmp-apps/src/environments/environment'; +import { + redrawPlots, + resetPlotSettings, +} from 'projects/nshmp-apps/src/shared/utils/facade.utils'; +import {catchError} from 'rxjs'; import {FormControls} from '../models/form-controls.model'; -import {actions} from './app.actions'; -import {distanceAppFeature} from './app.reducer'; +import { + DEFAULT_FORM_VALUES, + defaultPlots, + usageFormValues, +} from '../utils/app.default-values'; +import {serviceResponseToPlotData} from '../utils/response.handler'; +import {AppState, INITIAL_STATE} from './app.state'; /** * Entrypoint to NGRX store for GM vs distance application. @@ -21,76 +36,232 @@ import {distanceAppFeature} from './app.reducer'; providedIn: 'root', }) export class GmmDistanceAppFacade { - constructor(private store: Store) {} + readonly formGroup = this.formBuilder.group({ + ...DEFAULT_FORM_VALUES, + gmmSource: [], + MwMulti: [], + vs30Multi: [], + }) as FormGroupControls<FormControls>; - /** - * Returns the control panel form. - */ - get controlForm$(): Observable<FormGroupState<FormControls>> { - return this.store.pipe(select(distanceAppFeature.selectControlForm)); + readonly state = signal<AppState>(INITIAL_STATE); + + /** nshmp-ws base URL */ + private baseUrl = environment.webServices.data.url; + /** GMM service URL */ + private serviceUrl = `${this.baseUrl}${environment.webServices.data.services.gmmDistance}`; + + constructor( + private formBuilder: FormBuilder, + private nshmpService: NshmpService, + private spinnerService: SpinnerService + ) { + this.addValidators(this.formGroup); + this.formGroup.controls.gmmSource.setValue([]); + this.formGroup.controls.showEpistemicUncertainty.disable(); } /** * Returns the mean plot state. */ - get meanPlotState$(): Observable<NshmpPlot> { - return this.store.pipe(select(distanceAppFeature.selectMeanPlot)); + get meanPlotState(): Signal<NshmpPlot> { + return computed(() => this.state().plots?.get(gmmUtils.PlotType.MEANS)); } - get serviceCallInfo$(): Observable<ServiceCallInfo> { - return this.store.pipe(select(distanceAppFeature.selectServiceCallInfo)); + get serviceCallInfo(): Signal<ServiceCallInfo> { + return computed(() => this.state().serviceCallInfo); } /** * Returns the Gmm distance service responses. */ - get serviceResponse$(): Observable<GmmDistanceResponse[]> { - return this.store.pipe(select(distanceAppFeature.selectServiceResponses)); + get serviceResponse(): Signal<GmmDistanceResponse[]> { + return computed(() => this.state().serviceResponses); } - get supportedImts$(): Observable<EnumParameterValues[]> { - return this.store.pipe(select(distanceAppFeature.selectSupportedImts)); + get supportedImts(): Signal<EnumParameterValues[]> { + return computed(() => this.state().supportedImts); } /** * Returns the Gmm distance usage response. */ - get usage$(): Observable<GmmDistanceUsage> { - return this.store.pipe(select(distanceAppFeature.selectUsageResponses)); + get usage(): Signal<GmmDistanceUsage> { + return computed(() => this.state().usageResponse); } /** * Calls the Gmm distance service. */ callService(): void { - this.store.dispatch(actions.callServices()); + if (this.formGroup.invalid) { + return; + } + + this.spinnerService.show(SpinnerService.MESSAGE_SERVICE); + + const urls = gmmUtils.serviceEndpoints( + this.serviceUrl, + this.formGroup.getRawValue(), + this.formGroup.getRawValue().multiSelectableParam + ); + + this.nshmpService + .callServices$<GmmDistanceResponse>(urls) + .pipe(catchError((error: Error) => this.nshmpService.throwError$(error))) + .subscribe(serviceResponses => + this.handleServiceResponses(serviceResponses) + ); } /** * Initialize the application. */ init(): void { - this.store.dispatch(actions.init()); + this.spinnerService.show(SpinnerService.MESSAGE_METADATA); + + this.nshmpService + .callService$<GmmDistanceUsage>(this.serviceUrl) + .pipe(catchError((error: Error) => this.nshmpService.throwError$(error))) + .subscribe(usageResponse => this.handleUsageResponse(usageResponse)); } /** * Redraw the plot. */ plotRedraw(): void { - this.store.dispatch(actions.plotRedraw()); + this.updateState({ + plots: redrawPlots(this.state().plots), + }); } /** * Reset the control panel. */ resetControlPanel(): void { - this.store.dispatch(actions.resetControlPanel()); + this.formGroup.reset( + usageFormValues(this.state().usageResponse.response.parameters) + ); } /** * Reset the plot settings. */ resetPlotSettings(): void { - this.store.dispatch(actions.resetSettings()); + resetPlotSettings({ + currentPlots: this.state().plots, + defaultPlots: defaultPlots(), + }); + } + + private handleServiceResponses( + serviceResponses: GmmDistanceResponse[] + ): void { + const means = serviceResponses.map(s => s.response.means); + const sigmas = serviceResponses.map(s => s.response.sigmas); + const hasLogicTree = gmmUtils.hasTree([...means, ...sigmas]); + + if (hasLogicTree) { + this.formGroup.controls.showEpistemicUncertainty.enable(); + } else { + this.formGroup.controls.showEpistemicUncertainty.disable(); + } + + this.updateState({ + serviceCallInfo: gmmUtils.serviceCallInfo({ + multiSelectableParam: this.formGroup.getRawValue().multiSelectableParam, + serviceName: this.state().serviceCallInfo.serviceName, + serviceResponses, + serviceUrl: this.serviceUrl, + values: this.formGroup.getRawValue(), + }), + serviceResponses, + }); + + this.createPlots(); + + this.spinnerService.remove(); + } + + /** + * Reset the state. + */ + resetState(): void { + const serviceCallInfo = gmmUtils.serviceCallInfo({ + multiSelectableParam: this.formGroup.getRawValue().multiSelectableParam, + serviceName: this.state().serviceCallInfo.serviceName, + serviceResponses: this.state().serviceResponses, + serviceUrl: this.serviceUrl, + values: this.formGroup.getRawValue(), + }); + + this.updateState({ + serviceCallInfo, + serviceResponses: null, + }); + + this.createPlots(); + } + + /** + * Create plots based on current state and form group. + */ + createPlots(): void { + this.updateState({ + plots: serviceResponseToPlotData(this.state(), this.formGroup), + }); + } + + /** + * Update state. + * + * @param state Partial new state to update + */ + updateState(state: Partial<AppState>): void { + this.state.set({ + ...this.state(), + ...state, + }); + } + + private handleUsageResponse(usageResponse: GmmDistanceUsage): void { + this.spinnerService.remove(); + const parameters = usageResponse.response.parameters; + const values = usageFormValues(parameters); + + this.formGroup.patchValue({ + ...values, + }); + + const serviceCallInfo: ServiceCallInfo = { + ...this.state().serviceCallInfo, + usage: [this.serviceUrl], + }; + + this.updateState({ + serviceCallInfo, + usageResponse, + }); + + this.spinnerService.remove(); + } + + /** + * Add validators to form controls. + * + * @param form The form group + */ + private addValidators(form: FormGroupControls<FormControls>): void { + const required = (control: AbstractControl) => Validators.required(control); + + form.controls.Mw.addValidators(required); + form.controls.dip.addValidators(required); + form.controls.gmmSource.addValidators(required); + form.controls.imt.addValidators(required); + form.controls.multiSelectableParam.addValidators(required); + form.controls.vs30.addValidators(required); + form.controls.width.addValidators(required); + form.controls.zTor.addValidators(required); + + form.updateValueAndValidity(); } } diff --git a/projects/nshmp-apps/src/app/gmm/distance/state/app.state.ts b/projects/nshmp-apps/src/app/gmm/distance/state/app.state.ts index 4a63b64a7..da369801d 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/state/app.state.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/state/app.state.ts @@ -1,40 +1,18 @@ -import {ServiceCallInfo} from '@ghsc/nshmp-lib-ng/nshmp'; +import {ServiceCallInfo} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; import { GmmDistanceResponse, GmmDistanceUsage, } from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; import {EnumParameterValues} from '@ghsc/nshmp-utils-ts/libs/nshmp-ws-utils/metadata'; -import { - createFormGroupState, - disable, - FormGroupState, - updateGroup, -} from 'ngrx-forms'; -import {SharedAppState} from 'projects/nshmp-apps/src/shared/state/shared'; - -import {FormControls} from '../models/form-controls.model'; -import {DEFAULT_FORM_VALUES, defaultPlots} from '../utils/app.default-values'; - -/** Control form id for ngrx-forms */ -export const CONTROL_FORM_ID = '[ngrx-forms] GMM Distance App Control Form'; -const formState = createFormGroupState<FormControls>(CONTROL_FORM_ID, { - ...DEFAULT_FORM_VALUES, -}); - -/** - * Initial state for the control panel - */ -export const INITIAL_CONTROL_FORM_STATE = updateGroup<FormControls>({ - showEpistemicUncertainty: disable, -})(formState); +import {defaultPlots} from '../utils/app.default-values'; /** * GMM distance app state */ -export interface AppState extends SharedAppState { - /** Control panel form field state */ - controlForm: FormGroupState<FormControls>; +export interface AppState { + plots: Map<string, NshmpPlot>; /** Service call info */ serviceCallInfo: ServiceCallInfo; /** GMM service response */ @@ -42,21 +20,20 @@ export interface AppState extends SharedAppState { /** Supported IMTs */ supportedImts: EnumParameterValues[]; /** GMM usages */ - usageResponses: GmmDistanceUsage; + usageResponse: GmmDistanceUsage; } /** * GMM distance app inital state */ export const INITIAL_STATE: AppState = { - controlForm: INITIAL_CONTROL_FORM_STATE, plots: defaultPlots(), serviceCallInfo: { serviceCalls: [], serviceName: 'Ground Motion vs. Distance', - usage: null, + usage: [], }, serviceResponses: [], supportedImts: [], - usageResponses: null, + usageResponse: null, }; diff --git a/projects/nshmp-apps/src/app/gmm/distance/utils/app.default-values.ts b/projects/nshmp-apps/src/app/gmm/distance/utils/app.default-values.ts index 7f7d99165..ec3b7358f 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/utils/app.default-values.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/utils/app.default-values.ts @@ -1,10 +1,13 @@ -import {GmmSource, gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; -import {NshmpPlot, NshmpPlotSettings, plotUtils} from '@ghsc/nshmp-lib-ng/plot'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; +import { + NshmpPlot, + NshmpPlotSettings, + plotUtils, +} from '@ghsc/nshmp-lib-no-ngrx/plot'; import { GmmDistanceUsageParameters, GmmGroupType, } from '@ghsc/nshmp-utils-ts/libs/nshmp-ws/gmm-services'; -import {box, createFormGroupState} from 'ngrx-forms'; import {FormControlIds, FormControls} from '../models/form-controls.model'; @@ -16,16 +19,16 @@ export const MEAN_PLOT_SETTING_ID = '[ngrx-forms] Means Plot Settings Form'; export const DEFAULT_FORM_VALUES: FormControls = { dip: null, gmmGroupType: GmmGroupType.ACTIVE_CRUST, - gmmSource: box([] as GmmSource[]), + gmmSource: [], imt: 'default', multiSelectableParam: FormControlIds.GMM, Mw: null, - MwMulti: box([] as number[]), + MwMulti: [], rMax: 300, rMin: 0.1, showEpistemicUncertainty: false, vs30: null, - vs30Multi: box([] as number[]), + vs30Multi: [], width: null, z1p0: null, z2p5: null, @@ -38,7 +41,7 @@ export const DEFAULT_FORM_VALUES: FormControls = { * * @param parameters The service parameters */ -export function defaultFormValues( +export function usageFormValues( parameters: GmmDistanceUsageParameters ): FormControls { return { @@ -61,12 +64,7 @@ export function defaultPlots(): Map<string, NshmpPlot> { plots.set(gmmUtils.PlotType.MEANS, { label: 'Means', plotData: meansPlotData, - settingsForm: createFormGroupState<NshmpPlotSettings>( - MEAN_PLOT_SETTING_ID, - { - ...meansPlotSettingsForm, - } - ), + settingsForm: plotUtils.plotSettingsToFormGroup(meansPlotSettingsForm), }); return new Map(plots); diff --git a/projects/nshmp-apps/src/app/gmm/distance/utils/response.handler.ts b/projects/nshmp-apps/src/app/gmm/distance/utils/response.handler.ts index 39927a713..622dc0df4 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/utils/response.handler.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/utils/response.handler.ts @@ -1,9 +1,12 @@ -import {gmmUtils} from '@ghsc/nshmp-lib-ng/gmm'; -import {NshmpPlot} from '@ghsc/nshmp-lib-ng/plot'; +import {gmmUtils} from '@ghsc/nshmp-lib-no-ngrx/gmm'; +import {FormGroupControls} from '@ghsc/nshmp-lib-no-ngrx/nshmp'; +import {NshmpPlot} from '@ghsc/nshmp-lib-no-ngrx/plot'; import {XySequence} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/data'; import {Imt, imtToString} from '@ghsc/nshmp-utils-ts/libs/nshmp-lib/gmm'; +import {FormControls} from '../models/form-controls.model'; import {AppState} from '../state/app.state'; +import {defaultPlots} from './app.default-values'; /** * Transform Gmm distance service responses to plots. @@ -11,19 +14,28 @@ import {AppState} from '../state/app.state'; * @param state The application state */ export function serviceResponseToPlotData( - state: AppState + state: AppState, + form: FormGroupControls<FormControls> ): Map<string, NshmpPlot> { - if (state.serviceResponses.length === 0) { - return state.plots; + if (state.serviceResponses === null || state.serviceResponses?.length === 0) { + return defaultPlots(); } const plots = new Map<string, NshmpPlot>(); + const formValues = form.getRawValue(); const meanPlot = state.plots.get(gmmUtils.PlotType.MEANS); const hoverTemplate = '%{x} km, %{y} AFE'; const title = `Ground Motion vs. Distance: ${imtToString( - state.controlForm.value.imt as Imt + formValues.imt as Imt )}`; - const formValues = state.controlForm.value; + + meanPlot.settingsForm.patchValue({ + layout: { + title: { + text: title, + }, + }, + }); const responses: gmmUtils.GmmResponse<XySequence, number[]>[] = state.serviceResponses.map(serviceResponse => ({ -- GitLab From 10d475f2fb84d256203ab7f6a18145bd5b8b1abe Mon Sep 17 00:00:00 2001 From: Brandon Clayton <bclayton@usgs.gov> Date: Thu, 25 Jul 2024 16:00:26 -0600 Subject: [PATCH 3/4] add providers for tests --- .../distance/components/content/content.component.spec.ts | 7 ++----- .../control-panel/control-panel.component.spec.ts | 4 ++-- .../parameter-summary/parameter-summary.component.spec.ts | 4 ++-- .../gmm/distance/components/plots/plots.component.spec.ts | 7 +------ .../components/settings/settings.component.spec.ts | 4 ++-- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.spec.ts b/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.spec.ts index f6d805b1b..87deffc6a 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.spec.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/content/content.component.spec.ts @@ -1,6 +1,6 @@ +import {provideHttpClient} from '@angular/common/http'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {provideNoopAnimations} from '@angular/platform-browser/animations'; -import {provideMockStore} from '@ngrx/store/testing'; import {ContentComponent} from './content.component'; @@ -11,10 +11,7 @@ describe('GmmDistanceContentComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ContentComponent], - providers: [ - provideMockStore({initialState: {}}), - provideNoopAnimations(), - ], + providers: [provideNoopAnimations(), provideHttpClient()], teardown: {destroyAfterEach: false}, }).compileComponents(); })); diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.spec.ts b/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.spec.ts index 854ada744..627b07dcb 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.spec.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/control-panel/control-panel.component.spec.ts @@ -1,6 +1,6 @@ import {provideHttpClient} from '@angular/common/http'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; -import {provideMockStore} from '@ngrx/store/testing'; +import {provideNoopAnimations} from '@angular/platform-browser/animations'; import {ControlPanelComponent} from './control-panel.component'; @@ -11,7 +11,7 @@ describe('GmmDistanceControlPanelComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ControlPanelComponent], - providers: [provideMockStore({initialState: {}}), provideHttpClient()], + providers: [provideHttpClient(), provideNoopAnimations()], teardown: {destroyAfterEach: false}, }).compileComponents(); })); diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.spec.ts b/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.spec.ts index d8af63c07..b8d5719b6 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.spec.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/parameter-summary/parameter-summary.component.spec.ts @@ -1,5 +1,5 @@ +import {provideHttpClient} from '@angular/common/http'; import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {provideMockStore} from '@ngrx/store/testing'; import {ParameterSummaryComponent} from './parameter-summary.component'; @@ -10,7 +10,7 @@ describe('ParameterSummaryComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ParameterSummaryComponent], - providers: [provideMockStore({initialState: {}})], + providers: [provideHttpClient()], }).compileComponents(); }); diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.spec.ts b/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.spec.ts index feb6ffa8a..609ca30e2 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.spec.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/plots/plots.component.spec.ts @@ -1,7 +1,6 @@ import {provideHttpClient} from '@angular/common/http'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {provideNoopAnimations} from '@angular/platform-browser/animations'; -import {provideMockStore} from '@ngrx/store/testing'; import {PlotsComponent} from './plots.component'; @@ -12,11 +11,7 @@ describe('GmmDistancePlotsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [PlotsComponent], - providers: [ - provideMockStore({initialState: {}}), - provideHttpClient(), - provideNoopAnimations(), - ], + providers: [provideHttpClient(), provideNoopAnimations()], teardown: {destroyAfterEach: false}, }).compileComponents(); })); diff --git a/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.spec.ts b/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.spec.ts index 8f687875e..babda5391 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.spec.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/components/settings/settings.component.spec.ts @@ -1,6 +1,6 @@ import {provideHttpClient} from '@angular/common/http'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; -import {provideMockStore} from '@ngrx/store/testing'; +import {provideNoopAnimations} from '@angular/platform-browser/animations'; import {SettingsComponent} from './settings.component'; @@ -11,7 +11,7 @@ describe('GmmDistancePlotSettingsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [SettingsComponent], - providers: [provideMockStore({initialState: {}}), provideHttpClient()], + providers: [provideHttpClient(), provideNoopAnimations()], teardown: {destroyAfterEach: false}, }).compileComponents(); })); -- GitLab From 988733a88b5ceb9f012026d3f99013bcd3f5dee3 Mon Sep 17 00:00:00 2001 From: Brandon Clayton <bclayton@usgs.gov> Date: Fri, 26 Jul 2024 11:15:46 -0600 Subject: [PATCH 4/4] organize --- .../src/app/gmm/distance/state/app.facade.ts | 127 +++++++++--------- 1 file changed, 63 insertions(+), 64 deletions(-) diff --git a/projects/nshmp-apps/src/app/gmm/distance/state/app.facade.ts b/projects/nshmp-apps/src/app/gmm/distance/state/app.facade.ts index 0a17e865d..f13fbfb26 100644 --- a/projects/nshmp-apps/src/app/gmm/distance/state/app.facade.ts +++ b/projects/nshmp-apps/src/app/gmm/distance/state/app.facade.ts @@ -36,20 +36,19 @@ import {AppState, INITIAL_STATE} from './app.state'; providedIn: 'root', }) export class GmmDistanceAppFacade { + /** nshmp-ws base URL */ + private baseUrl = environment.webServices.data.url; + /** GMM service URL */ + private serviceUrl = `${this.baseUrl}${environment.webServices.data.services.gmmDistance}`; + readonly formGroup = this.formBuilder.group({ ...DEFAULT_FORM_VALUES, gmmSource: [], MwMulti: [], vs30Multi: [], }) as FormGroupControls<FormControls>; - readonly state = signal<AppState>(INITIAL_STATE); - /** nshmp-ws base URL */ - private baseUrl = environment.webServices.data.url; - /** GMM service URL */ - private serviceUrl = `${this.baseUrl}${environment.webServices.data.services.gmmDistance}`; - constructor( private formBuilder: FormBuilder, private nshmpService: NshmpService, @@ -113,6 +112,15 @@ export class GmmDistanceAppFacade { ); } + /** + * Create plots based on current state and form group. + */ + createPlots(): void { + this.updateState({ + plots: serviceResponseToPlotData(this.state(), this.formGroup), + }); + } + /** * Initialize the application. */ @@ -153,35 +161,6 @@ export class GmmDistanceAppFacade { }); } - private handleServiceResponses( - serviceResponses: GmmDistanceResponse[] - ): void { - const means = serviceResponses.map(s => s.response.means); - const sigmas = serviceResponses.map(s => s.response.sigmas); - const hasLogicTree = gmmUtils.hasTree([...means, ...sigmas]); - - if (hasLogicTree) { - this.formGroup.controls.showEpistemicUncertainty.enable(); - } else { - this.formGroup.controls.showEpistemicUncertainty.disable(); - } - - this.updateState({ - serviceCallInfo: gmmUtils.serviceCallInfo({ - multiSelectableParam: this.formGroup.getRawValue().multiSelectableParam, - serviceName: this.state().serviceCallInfo.serviceName, - serviceResponses, - serviceUrl: this.serviceUrl, - values: this.formGroup.getRawValue(), - }), - serviceResponses, - }); - - this.createPlots(); - - this.spinnerService.remove(); - } - /** * Reset the state. */ @@ -202,15 +181,6 @@ export class GmmDistanceAppFacade { this.createPlots(); } - /** - * Create plots based on current state and form group. - */ - createPlots(): void { - this.updateState({ - plots: serviceResponseToPlotData(this.state(), this.formGroup), - }); - } - /** * Update state. * @@ -223,6 +193,55 @@ export class GmmDistanceAppFacade { }); } + /** + * Add validators to form controls. + * + * @param form The form group + */ + private addValidators(form: FormGroupControls<FormControls>): void { + const required = (control: AbstractControl) => Validators.required(control); + + form.controls.Mw.addValidators(required); + form.controls.dip.addValidators(required); + form.controls.gmmSource.addValidators(required); + form.controls.imt.addValidators(required); + form.controls.multiSelectableParam.addValidators(required); + form.controls.vs30.addValidators(required); + form.controls.width.addValidators(required); + form.controls.zTor.addValidators(required); + + form.updateValueAndValidity(); + } + + private handleServiceResponses( + serviceResponses: GmmDistanceResponse[] + ): void { + const means = serviceResponses.map(s => s.response.means); + const sigmas = serviceResponses.map(s => s.response.sigmas); + const hasLogicTree = gmmUtils.hasTree([...means, ...sigmas]); + + if (hasLogicTree) { + this.formGroup.controls.showEpistemicUncertainty.enable(); + } else { + this.formGroup.controls.showEpistemicUncertainty.disable(); + } + + this.updateState({ + serviceCallInfo: gmmUtils.serviceCallInfo({ + multiSelectableParam: this.formGroup.getRawValue().multiSelectableParam, + serviceName: this.state().serviceCallInfo.serviceName, + serviceResponses, + serviceUrl: this.serviceUrl, + values: this.formGroup.getRawValue(), + }), + serviceResponses, + }); + + this.createPlots(); + + this.spinnerService.remove(); + } + private handleUsageResponse(usageResponse: GmmDistanceUsage): void { this.spinnerService.remove(); const parameters = usageResponse.response.parameters; @@ -244,24 +263,4 @@ export class GmmDistanceAppFacade { this.spinnerService.remove(); } - - /** - * Add validators to form controls. - * - * @param form The form group - */ - private addValidators(form: FormGroupControls<FormControls>): void { - const required = (control: AbstractControl) => Validators.required(control); - - form.controls.Mw.addValidators(required); - form.controls.dip.addValidators(required); - form.controls.gmmSource.addValidators(required); - form.controls.imt.addValidators(required); - form.controls.multiSelectableParam.addValidators(required); - form.controls.vs30.addValidators(required); - form.controls.width.addValidators(required); - form.controls.zTor.addValidators(required); - - form.updateValueAndValidity(); - } } -- GitLab