From 30da831a92d9304579fb9aba99748d17b495e73a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr> Date: Fri, 9 Jul 2021 19:05:51 +0200 Subject: [PATCH] Search store => WIP --- client/src/app/instance/instance.component.ts | 10 +- client/src/app/instance/instance.reducer.ts | 5 + .../dataset/dataset-card.component.html | 3 +- .../dataset/dataset-card.component.ts | 11 +- .../dataset/dataset-tabs.component.ts | 2 +- .../containers/abstract-search.component.ts | 85 ++++++++ .../search/containers/criteria.component.ts | 84 +------- .../search/containers/dataset.component.html | 2 +- .../search/containers/dataset.component.ts | 138 +----------- .../search/containers/output.component.ts | 83 +------- .../search/containers/result.component.ts | 74 +------ .../app/instance/search/search.component.ts | 2 - .../store/actions/metamodel.actions.ts | 13 ++ .../instance/store/actions/search.actions.ts | 5 + .../src/app/instance/store/effects/index.ts | 2 + .../store/effects/metamodel.effects.ts | 57 +++++ .../instance/store/effects/search.effects.ts | 199 ++++++++++++++++-- .../store/reducers/metamodel.reducer.ts | 34 +++ .../instance/store/reducers/search.reducer.ts | 15 ++ .../store/selectors/metamodel.selector.ts | 28 +++ .../instance/store/selectors/samp.selector.ts | 8 +- .../store/selectors/search.selector.ts | 48 +++-- .../metamodel/reducers/attribute.reducer.ts | 3 +- 23 files changed, 514 insertions(+), 397 deletions(-) create mode 100644 client/src/app/instance/search/containers/abstract-search.component.ts create mode 100644 client/src/app/instance/store/actions/metamodel.actions.ts create mode 100644 client/src/app/instance/store/effects/metamodel.effects.ts create mode 100644 client/src/app/instance/store/reducers/metamodel.reducer.ts create mode 100644 client/src/app/instance/store/selectors/metamodel.selector.ts diff --git a/client/src/app/instance/instance.component.ts b/client/src/app/instance/instance.component.ts index 848e5e14..6d2d3146 100644 --- a/client/src/app/instance/instance.component.ts +++ b/client/src/app/instance/instance.component.ts @@ -7,13 +7,14 @@ * file that was distributed with this source code. */ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { Store } from '@ngrx/store'; import { UserProfile } from 'src/app/auth/user-profile.model'; import * as authActions from 'src/app/auth/auth.actions'; import * as authSelector from 'src/app/auth/auth.selector'; +import * as metamodelActions from './store/actions/metamodel.actions'; @Component({ selector: 'app-instance', @@ -23,7 +24,7 @@ import * as authSelector from 'src/app/auth/auth.selector'; * @class * @classdesc Instance container */ -export class InstanceComponent { +export class InstanceComponent implements OnInit { public links = [ { label: 'Home', icon: 'fas fa-home', routerLink: 'home' }, { label: 'Search', icon: 'fas fa-search', routerLink: 'search' }, @@ -40,6 +41,11 @@ export class InstanceComponent { this.userRoles = store.select(authSelector.selectUserRoles); } + ngOnInit() { + // Load datasetList, datasetFamilyList, SurveyList + Promise.resolve(null).then(() => this.store.dispatch(metamodelActions.loadInstanceMetamodel())); + } + login(): void { this.store.dispatch(authActions.login()); } diff --git a/client/src/app/instance/instance.reducer.ts b/client/src/app/instance/instance.reducer.ts index 1c3ad08a..7b96c807 100644 --- a/client/src/app/instance/instance.reducer.ts +++ b/client/src/app/instance/instance.reducer.ts @@ -9,18 +9,23 @@ import { combineReducers, createFeatureSelector } from '@ngrx/store'; +import { RouterReducerState } from 'src/app/custom-route-serializer'; +import * as metamodel from './store/reducers/metamodel.reducer'; import * as search from './store/reducers/search.reducer'; import * as samp from './store/reducers/samp.reducer'; export interface State { + metamodel: metamodel.State, search: search.State, samp: samp.State; } const reducers = { + metamodel: metamodel.metamodelReducer, search: search.searchReducer, samp: samp.sampReducer }; export const instanceReducer = combineReducers(reducers); export const getInstanceState = createFeatureSelector<State>('instance'); +export const selectRouterState = createFeatureSelector<RouterReducerState>('router'); diff --git a/client/src/app/instance/search/components/dataset/dataset-card.component.html b/client/src/app/instance/search/components/dataset/dataset-card.component.html index fbe4f48a..190c9bc9 100644 --- a/client/src/app/instance/search/components/dataset/dataset-card.component.html +++ b/client/src/app/instance/search/components/dataset/dataset-card.component.html @@ -24,7 +24,8 @@ </div> </div> <div class="col-auto"> - <button *ngIf="dataset.name !== datasetSelected" routerLink="/instance/{{ instanceSelected }}/search/dataset/{{ dataset.name }}" + <button *ngIf="dataset.name !== datasetSelected" + (click)="selectDataset(dataset.name)" class="btn btn-outline-secondary"> Select </button> diff --git a/client/src/app/instance/search/components/dataset/dataset-card.component.ts b/client/src/app/instance/search/components/dataset/dataset-card.component.ts index 6eac1690..ffeb52d4 100644 --- a/client/src/app/instance/search/components/dataset/dataset-card.component.ts +++ b/client/src/app/instance/search/components/dataset/dataset-card.component.ts @@ -7,7 +7,8 @@ * file that was distributed with this source code. */ -import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; +import { Component, Input, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core'; +import { Router } from '@angular/router'; import { Survey, Dataset } from 'src/app/metamodel/models'; @@ -25,5 +26,11 @@ export class DatasetCardComponent { @Input() dataset: Dataset; @Input() instanceSelected: string; @Input() datasetSelected: string; + + constructor(private router: Router) { } + + selectDataset(datasetName: string) { + this.router.routeReuseStrategy.shouldReuseRoute = () => false; + this.router.navigate([`/instance/${this.instanceSelected}/search/dataset/${datasetName}`]); + } } - \ No newline at end of file diff --git a/client/src/app/instance/search/components/dataset/dataset-tabs.component.ts b/client/src/app/instance/search/components/dataset/dataset-tabs.component.ts index 2ce71fe7..b2b87327 100644 --- a/client/src/app/instance/search/components/dataset/dataset-tabs.component.ts +++ b/client/src/app/instance/search/components/dataset/dataset-tabs.component.ts @@ -7,7 +7,7 @@ * file that was distributed with this source code. */ -import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; +import { Component, Input, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core'; import { Survey, Dataset, DatasetFamily } from 'src/app/metamodel/models'; diff --git a/client/src/app/instance/search/containers/abstract-search.component.ts b/client/src/app/instance/search/containers/abstract-search.component.ts new file mode 100644 index 00000000..8256337e --- /dev/null +++ b/client/src/app/instance/search/containers/abstract-search.component.ts @@ -0,0 +1,85 @@ +import { Directive, OnInit, OnDestroy } from '@angular/core'; + +import { Store } from '@ngrx/store'; +import { Observable, Subscription } from 'rxjs'; + +import { Criterion, SearchQueryParams } from '../../store/models'; +import { Dataset, Attribute, CriteriaFamily, OutputFamily, OutputCategory } from 'src/app/metamodel/models'; + +import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; +import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; +import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector'; +import * as criteriaFamilySelector from 'src/app/metamodel/selectors/criteria-family.selector'; +import * as outputFamilySelector from 'src/app/metamodel/selectors/output-family.selector'; +import * as outputCategorySelector from 'src/app/metamodel/selectors/output-category.selector'; +import * as searchActions from '../../store/actions/search.actions'; +import * as searchSelector from '../../store/selectors/search.selector'; + +@Directive() +export abstract class AbstractSearchComponent implements OnInit, OnDestroy { + public datasetSelected: Observable<string>; + public instanceSelected: Observable<string>; + public datasetListIsLoading: Observable<boolean>; + public datasetListIsLoaded: Observable<boolean>; + public datasetList: Observable<Dataset[]>; + public attributeList: Observable<Attribute[]>; + public attributeListIsLoading: Observable<boolean>; + public attributeListIsLoaded: Observable<boolean>; + public criteriaFamilyList: Observable<CriteriaFamily[]>; + public criteriaFamilyListIsLoading: Observable<boolean>; + public criteriaFamilyListIsLoaded: Observable<boolean>; + public outputFamilyList: Observable<OutputFamily[]>; + public outputFamilyListIsLoading: Observable<boolean>; + public outputFamilyListIsLoaded: Observable<boolean>; + public outputCategoryList: Observable<OutputCategory[]>; + public outputCategoryListIsLoading: Observable<boolean>; + public outputCategoryListIsLoaded: Observable<boolean>; + public pristine: Observable<boolean>; + public currentStep: Observable<string>; + public criteriaList: Observable<Criterion[]>; + public outputList: Observable<number[]>; + public queryParams: Observable<SearchQueryParams>; + + private attributeListIsLoadedSubscription: Subscription; + + constructor(protected store: Store<{ }>) { + this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute); + this.instanceSelected = store.select(instanceSelector.selectInstanceNameByRoute); + this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); + this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); + this.datasetList = store.select(datasetSelector.selectAllDatasets); + this.attributeList = store.select(attributeSelector.selectAllAttributes); + this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading); + this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded); + this.criteriaFamilyList = store.select(criteriaFamilySelector.selectAllCriteriaFamilies); + this.criteriaFamilyListIsLoading = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoading); + this.criteriaFamilyListIsLoaded = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoaded); + this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilies); + this.outputFamilyListIsLoading = store.select(outputFamilySelector.selectOutputFamilyListIsLoading); + this.outputFamilyListIsLoaded = store.select(outputFamilySelector.selectOutputFamilyListIsLoaded); + this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategories); + this.outputCategoryListIsLoading = store.select(outputCategorySelector.selectOutputCategoryListIsLoading); + this.outputCategoryListIsLoaded = store.select(outputCategorySelector.selectOutputCategoryListIsLoaded); + this.pristine = this.store.select(searchSelector.selectPristine); + this.currentStep = this.store.select(searchSelector.selectCurrentStep); + this.criteriaList = this.store.select(searchSelector.selectCriteriaList); + this.outputList = this.store.select(searchSelector.selectOutputList); + this.queryParams = this.store.select(searchSelector.selectQueryParams); + } + + ngOnInit() { + // Create a micro task that is processed after the current synchronous code + // This micro task prevent the expression has changed after view init error + Promise.resolve(null).then(() => this.store.dispatch(searchActions.initSearch())); + this.attributeListIsLoadedSubscription = this.attributeListIsLoaded.subscribe(attributeListIsLoaded => { + if (attributeListIsLoaded) { + this.store.dispatch(searchActions.loadDefaultOutputList()); + this.store.dispatch(searchActions.loadDefaultCriteriaList()); + } + }); + } + + ngOnDestroy() { + this.attributeListIsLoadedSubscription.unsubscribe(); + } +} diff --git a/client/src/app/instance/search/containers/criteria.component.ts b/client/src/app/instance/search/containers/criteria.component.ts index 4bc182eb..a226c24e 100644 --- a/client/src/app/instance/search/containers/criteria.component.ts +++ b/client/src/app/instance/search/containers/criteria.component.ts @@ -7,26 +7,11 @@ * file that was distributed with this source code. */ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; - -import { Criterion, SearchQueryParams } from '../../store/models'; -import { Dataset, CriteriaFamily, OutputFamily, Attribute, OutputCategory } from 'src/app/metamodel/models'; -import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; -import * as datasetActions from 'src/app/metamodel/actions/dataset.actions'; -import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; -import * as attributeActions from 'src/app/metamodel/actions/attribute.actions'; -import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector'; -import * as criteriaFamilyActions from 'src/app/metamodel/actions/criteria-family.actions'; -import * as criteriaFamilySelector from 'src/app/metamodel/selectors/criteria-family.selector'; -import * as outputFamilyActions from 'src/app/metamodel/actions/output-family.actions'; -import * as outputFamilySelector from 'src/app/metamodel/selectors/output-family.selector'; -import * as outputCategoryActions from 'src/app/metamodel/actions/output-category.actions'; -import * as outputCategorySelector from 'src/app/metamodel/selectors/output-category.selector'; +import { AbstractSearchComponent } from './abstract-search.component'; +import { Criterion } from '../../store/models'; import * as searchActions from '../../store/actions/search.actions'; -import * as searchSelector from '../../store/selectors/search.selector'; @Component({ selector: 'app-criteria', @@ -35,71 +20,12 @@ import * as searchSelector from '../../store/selectors/search.selector'; /** * @class * @classdesc Search criteria container. - * - * @implements OnInit */ -export class CriteriaComponent implements OnInit { - public datasetSelected: Observable<string>; - public instanceSelected: Observable<string>; - public datasetListIsLoading: Observable<boolean>; - public datasetListIsLoaded: Observable<boolean>; - public datasetList: Observable<Dataset[]>; - public attributeList: Observable<Attribute[]>; - public attributeListIsLoading: Observable<boolean>; - public attributeListIsLoaded: Observable<boolean>; - public criteriaFamilyList: Observable<CriteriaFamily[]>; - public criteriaFamilyListIsLoading: Observable<boolean>; - public criteriaFamilyListIsLoaded: Observable<boolean>; - public outputFamilyList: Observable<OutputFamily[]>; - public outputFamilyListIsLoading: Observable<boolean>; - public outputFamilyListIsLoaded: Observable<boolean>; - public outputCategoryList: Observable<OutputCategory[]>; - public outputCategoryListIsLoading: Observable<boolean>; - public outputCategoryListIsLoaded: Observable<boolean>; - public currentStep: Observable<string>; - public criteriaList: Observable<Criterion[]>; - public outputList: Observable<number[]>; - public queryParams: Observable<SearchQueryParams>; - - constructor(private store: Store<{ }>) { - this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute); - this.instanceSelected = store.select(instanceSelector.selectInstanceNameByRoute); - this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); - this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); - this.datasetList = store.select(datasetSelector.selectAllDatasets); - this.attributeList = store.select(attributeSelector.selectAllAttributes); - this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading); - this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded); - this.criteriaFamilyList = store.select(criteriaFamilySelector.selectAllCriteriaFamilies); - this.criteriaFamilyListIsLoading = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoading); - this.criteriaFamilyListIsLoaded = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoaded); - this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilies); - this.outputFamilyListIsLoading = store.select(outputFamilySelector.selectOutputFamilyListIsLoading); - this.outputFamilyListIsLoaded = store.select(outputFamilySelector.selectOutputFamilyListIsLoaded); - this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategories); - this.outputCategoryListIsLoading = store.select(outputCategorySelector.selectOutputCategoryListIsLoading); - this.outputCategoryListIsLoaded = store.select(outputCategorySelector.selectOutputCategoryListIsLoaded); - this.currentStep = this.store.select(searchSelector.selectCurrentStep); - this.criteriaList = this.store.select(searchSelector.selectCriteriaList); - this.outputList = this.store.select(searchSelector.selectOutputList); - this.queryParams = this.store.select(searchSelector.selectQueryParams); - } - +export class CriteriaComponent extends AbstractSearchComponent { ngOnInit() { - // Create a micro task that is processed after the current synchronous code - // This micro task prevent the expression has changed after view init error Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'criteria' }))); Promise.resolve(null).then(() => this.store.dispatch(searchActions.checkCriteria())); - - this.store.dispatch(datasetActions.loadDatasetList()); - this.datasetSelected.subscribe(dname => { - if (dname) { - this.store.dispatch(attributeActions.loadAttributeList()); - this.store.dispatch(criteriaFamilyActions.loadCriteriaFamilyList()); - this.store.dispatch(outputFamilyActions.loadOutputFamilyList()); - this.store.dispatch(outputCategoryActions.loadOutputCategoryList()); - } - }); + super.ngOnInit(); } /** diff --git a/client/src/app/instance/search/containers/dataset.component.html b/client/src/app/instance/search/containers/dataset.component.html index 53d7479f..b86d5815 100644 --- a/client/src/app/instance/search/containers/dataset.component.html +++ b/client/src/app/instance/search/containers/dataset.component.html @@ -26,7 +26,7 @@ </div> <div class="col-12 col-md-4 col-lg-3 pt-2"> <app-spinner *ngIf="(attributeListIsLoading | async) || (criteriaFamilyListIsLoading | async) || (outputFamilyListIsLoading | async) || (outputCategoryListIsLoading | async)"></app-spinner> - <app-summary *ngIf="(attributeListIsLoaded | async) && (criteriaFamilyListIsLoaded | async) && (outputFamilyListIsLoaded | async) && (outputCategoryListIsLoaded | async)" + <app-summary *ngIf="!(pristine | async) && (attributeListIsLoaded | async) && (criteriaFamilyListIsLoaded | async) && (outputFamilyListIsLoaded | async) && (outputCategoryListIsLoaded | async)" [currentStep]="currentStep | async" [datasetSelected]="datasetSelected | async" [datasetList]="datasetList | async" diff --git a/client/src/app/instance/search/containers/dataset.component.ts b/client/src/app/instance/search/containers/dataset.component.ts index b839b48a..bff120ab 100644 --- a/client/src/app/instance/search/containers/dataset.component.ts +++ b/client/src/app/instance/search/containers/dataset.component.ts @@ -7,31 +7,17 @@ * file that was distributed with this source code. */ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component} from '@angular/core'; import { Store } from '@ngrx/store'; -import { Observable, Subscription } from 'rxjs'; +import { Observable } from 'rxjs'; -import { Criterion, SearchQueryParams } from '../../store/models'; -import { Survey, Dataset, Attribute, DatasetFamily, CriteriaFamily, OutputFamily, OutputCategory } from 'src/app/metamodel/models'; +import { AbstractSearchComponent } from './abstract-search.component'; +import { Survey, DatasetFamily } from 'src/app/metamodel/models'; +import * as searchActions from '../../store/actions/search.actions'; import * as authSelector from 'src/app/auth/auth.selector'; -import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; -import * as datasetFamilyActions from 'src/app/metamodel/actions/dataset-family.actions'; import * as datasetFamilySelector from 'src/app/metamodel/selectors/dataset-family.selector'; -import * as datasetActions from 'src/app/metamodel/actions/dataset.actions'; -import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; -import * as surveyActions from 'src/app/metamodel/actions/survey.actions'; import * as surveySelector from 'src/app/metamodel/selectors/survey.selector'; -import * as attributeActions from 'src/app/metamodel/actions/attribute.actions'; -import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector'; -import * as criteriaFamilyActions from 'src/app/metamodel/actions/criteria-family.actions'; -import * as criteriaFamilySelector from 'src/app/metamodel/selectors/criteria-family.selector'; -import * as outputFamilyActions from 'src/app/metamodel/actions/output-family.actions'; -import * as outputFamilySelector from 'src/app/metamodel/selectors/output-family.selector'; -import * as outputCategoryActions from 'src/app/metamodel/actions/output-category.actions'; -import * as outputCategorySelector from 'src/app/metamodel/selectors/output-category.selector'; -import * as searchActions from '../../store/actions/search.actions'; -import * as searchSelector from '../../store/selectors/search.selector'; @Component({ selector: 'app-dataset', @@ -40,135 +26,29 @@ import * as searchSelector from '../../store/selectors/search.selector'; /** * @class * @classdesc Search dataset container. - * - * @implements OnInit */ -export class DatasetComponent implements OnInit, OnDestroy { +export class DatasetComponent extends AbstractSearchComponent { public isAuthenticated: Observable<boolean>; - public datasetSelected: Observable<string>; - public instanceSelected: Observable<string>; public datasetFamilyListIsLoading: Observable<boolean>; public datasetFamilyListIsLoaded: Observable<boolean>; public datasetFamilyList: Observable<DatasetFamily[]>; - public datasetListIsLoading: Observable<boolean>; - public datasetListIsLoaded: Observable<boolean>; - public datasetList: Observable<Dataset[]>; public surveyListIsLoading: Observable<boolean>; public surveyListIsLoaded: Observable<boolean>; public surveyList: Observable<Survey[]>; - public attributeList: Observable<Attribute[]>; - public attributeListIsLoading: Observable<boolean>; - public attributeListIsLoaded: Observable<boolean>; - public criteriaFamilyList: Observable<CriteriaFamily[]>; - public criteriaFamilyListIsLoading: Observable<boolean>; - public criteriaFamilyListIsLoaded: Observable<boolean>; - public outputFamilyList: Observable<OutputFamily[]>; - public outputFamilyListIsLoading: Observable<boolean>; - public outputFamilyListIsLoaded: Observable<boolean>; - public outputCategoryList: Observable<OutputCategory[]>; - public outputCategoryListIsLoading: Observable<boolean>; - public outputCategoryListIsLoaded: Observable<boolean>; - public currentStep: Observable<string>; - public criteriaList: Observable<Criterion[]>; - public outputList: Observable<number[]>; - public queryParams: Observable<SearchQueryParams>; - datasetSelectedSubscription: Subscription; - attributeListSubscription: Subscription; - - constructor(private store: Store<{ }>) { + constructor(protected store: Store<{ }>) { + super(store); this.isAuthenticated = store.select(authSelector.selectIsAuthenticated); - this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute); - this.instanceSelected = store.select(instanceSelector.selectInstanceNameByRoute); this.datasetFamilyListIsLoading = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoading); this.datasetFamilyListIsLoaded = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoaded); this.datasetFamilyList = store.select(datasetFamilySelector.selectAllDatasetFamilies); - this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); - this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); - this.datasetList = store.select(datasetSelector.selectAllDatasets); this.surveyListIsLoading = store.select(surveySelector.selectSurveyListIsLoading); this.surveyListIsLoaded = store.select(surveySelector.selectSurveyListIsLoaded); this.surveyList = store.select(surveySelector.selectAllSurveys); - this.attributeList = store.select(attributeSelector.selectAllAttributes); - this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading); - this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded); - this.criteriaFamilyList = store.select(criteriaFamilySelector.selectAllCriteriaFamilies); - this.criteriaFamilyListIsLoading = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoading); - this.criteriaFamilyListIsLoaded = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoaded); - this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilies); - this.outputFamilyListIsLoading = store.select(outputFamilySelector.selectOutputFamilyListIsLoading); - this.outputFamilyListIsLoaded = store.select(outputFamilySelector.selectOutputFamilyListIsLoaded); - this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategories); - this.outputCategoryListIsLoading = store.select(outputCategorySelector.selectOutputCategoryListIsLoading); - this.outputCategoryListIsLoaded = store.select(outputCategorySelector.selectOutputCategoryListIsLoaded); - this.currentStep = this.store.select(searchSelector.selectCurrentStep); - this.criteriaList = this.store.select(searchSelector.selectCriteriaList); - this.outputList = this.store.select(searchSelector.selectOutputList); - this.queryParams = this.store.select(searchSelector.selectQueryParams); } ngOnInit() { - // Create a micro task that is processed after the current synchronous code - // This micro task prevent the expression has changed after view init error Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'dataset' }))); - - this.store.dispatch(datasetFamilyActions.loadDatasetFamilyList()); - this.store.dispatch(datasetActions.loadDatasetList()); - this.store.dispatch(surveyActions.loadSurveyList()); - this.datasetSelectedSubscription = this.datasetSelected.subscribe(dname => { - if (dname) { - this.store.dispatch(attributeActions.loadAttributeList()); - this.store.dispatch(criteriaFamilyActions.loadCriteriaFamilyList()); - this.store.dispatch(outputFamilyActions.loadOutputFamilyList()); - this.store.dispatch(outputCategoryActions.loadOutputCategoryList()); - } - }); - - this.attributeListSubscription = this.attributeList.subscribe(attributeList => { - if (attributeList) { - // Generate outputList - const defaultOutputList = attributeList - .filter(attribute => attribute.selected && attribute.id_output_category) - .map(attribute => attribute.id); - this.store.dispatch(searchActions.updateOutputList({ outputList: defaultOutputList })); - - // Generate criteriaList - const defaultCriteriaList = attributeList - .filter(attribute => attribute.id_criteria_family && attribute.search_type && attribute.min) - .map(attribute => { - switch (attribute.search_type) { - case 'field': - case 'select': - case 'datalist': - case 'radio': - case 'date': - case 'date-time': - case 'time': - return { id: attribute.id, type: 'field', value: attribute.min.toString(), operator: attribute.operator }; - case 'list': - return { id: attribute.id, type: 'list', values: attribute.min.toString().split('|') }; - case 'between': - case 'between-date': - return { id: attribute.id, type: 'between', min: attribute.min.toString(), max: attribute.max.toString() }; - case 'select-multiple': - case 'checkbox': - const msValues = attribute.min.toString().split('|'); - const options = attribute.options.filter(option => msValues.includes(option.value)); - return { id: attribute.id, type: 'multiple', options }; - case 'json': - const [path, operator, value] = attribute.min.toString().split('|'); - return { id: attribute.id, type: 'json', path, operator, value }; - default: - return null; - } - }); - this.store.dispatch(searchActions.updateCriteriaList({ criteriaList: defaultCriteriaList })); - } - }); - } - - ngOnDestroy() { - this.datasetSelectedSubscription.unsubscribe(); - this.attributeListSubscription.unsubscribe(); + super.ngOnInit(); } } diff --git a/client/src/app/instance/search/containers/output.component.ts b/client/src/app/instance/search/containers/output.component.ts index 69404cdf..2a8962bc 100644 --- a/client/src/app/instance/search/containers/output.component.ts +++ b/client/src/app/instance/search/containers/output.component.ts @@ -7,26 +7,10 @@ * file that was distributed with this source code. */ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; - -import { Criterion, SearchQueryParams } from '../../store/models'; -import { Dataset, CriteriaFamily, OutputFamily, Attribute, OutputCategory } from 'src/app/metamodel/models'; -import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; -import * as datasetActions from 'src/app/metamodel/actions/dataset.actions'; -import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; -import * as attributeActions from 'src/app/metamodel/actions/attribute.actions'; -import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector'; -import * as criteriaFamilyActions from 'src/app/metamodel/actions/criteria-family.actions'; -import * as criteriaFamilySelector from 'src/app/metamodel/selectors/criteria-family.selector'; -import * as outputFamilyActions from 'src/app/metamodel/actions/output-family.actions'; -import * as outputFamilySelector from 'src/app/metamodel/selectors/output-family.selector'; -import * as outputCategoryActions from 'src/app/metamodel/actions/output-category.actions'; -import * as outputCategorySelector from 'src/app/metamodel/selectors/output-category.selector'; +import { AbstractSearchComponent } from './abstract-search.component'; import * as searchActions from '../../store/actions/search.actions'; -import * as searchSelector from '../../store/selectors/search.selector'; @Component({ selector: 'app-output', @@ -35,71 +19,12 @@ import * as searchSelector from '../../store/selectors/search.selector'; /** * @class * @classdesc Search output container. - * - * @implements OnInit */ -export class OutputComponent implements OnInit { - public datasetSelected: Observable<string>; - public instanceSelected: Observable<string>; - public datasetListIsLoading: Observable<boolean>; - public datasetListIsLoaded: Observable<boolean>; - public datasetList: Observable<Dataset[]>; - public attributeList: Observable<Attribute[]>; - public attributeListIsLoading: Observable<boolean>; - public attributeListIsLoaded: Observable<boolean>; - public criteriaFamilyList: Observable<CriteriaFamily[]>; - public criteriaFamilyListIsLoading: Observable<boolean>; - public criteriaFamilyListIsLoaded: Observable<boolean>; - public outputFamilyList: Observable<OutputFamily[]>; - public outputFamilyListIsLoading: Observable<boolean>; - public outputFamilyListIsLoaded: Observable<boolean>; - public outputCategoryList: Observable<OutputCategory[]>; - public outputCategoryListIsLoading: Observable<boolean>; - public outputCategoryListIsLoaded: Observable<boolean>; - public currentStep: Observable<string>; - public criteriaList: Observable<Criterion[]>; - public outputList: Observable<number[]>; - public queryParams: Observable<SearchQueryParams>; - - constructor(private store: Store<{ }>) { - this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute); - this.instanceSelected = store.select(instanceSelector.selectInstanceNameByRoute); - this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); - this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); - this.datasetList = store.select(datasetSelector.selectAllDatasets); - this.attributeList = store.select(attributeSelector.selectAllAttributes); - this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading); - this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded); - this.criteriaFamilyList = store.select(criteriaFamilySelector.selectAllCriteriaFamilies); - this.criteriaFamilyListIsLoading = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoading); - this.criteriaFamilyListIsLoaded = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoaded); - this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilies); - this.outputFamilyListIsLoading = store.select(outputFamilySelector.selectOutputFamilyListIsLoading); - this.outputFamilyListIsLoaded = store.select(outputFamilySelector.selectOutputFamilyListIsLoaded); - this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategories); - this.outputCategoryListIsLoading = store.select(outputCategorySelector.selectOutputCategoryListIsLoading); - this.outputCategoryListIsLoaded = store.select(outputCategorySelector.selectOutputCategoryListIsLoaded); - this.currentStep = this.store.select(searchSelector.selectCurrentStep); - this.criteriaList = this.store.select(searchSelector.selectCriteriaList); - this.outputList = this.store.select(searchSelector.selectOutputList); - this.queryParams = this.store.select(searchSelector.selectQueryParams); - } - +export class OutputComponent extends AbstractSearchComponent { ngOnInit() { - // Create a micro task that is processed after the current synchronous code - // This micro task prevent the expression has changed after view init error Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'output' }))); Promise.resolve(null).then(() => this.store.dispatch(searchActions.checkOutput())); - - this.store.dispatch(datasetActions.loadDatasetList()); - this.datasetSelected.subscribe(dname => { - if (dname) { - this.store.dispatch(attributeActions.loadAttributeList()); - this.store.dispatch(criteriaFamilyActions.loadCriteriaFamilyList()); - this.store.dispatch(outputFamilyActions.loadOutputFamilyList()); - this.store.dispatch(outputCategoryActions.loadOutputCategoryList()); - } - }); + super.ngOnInit(); } /** diff --git a/client/src/app/instance/search/containers/result.component.ts b/client/src/app/instance/search/containers/result.component.ts index a947959e..0b94c8b5 100644 --- a/client/src/app/instance/search/containers/result.component.ts +++ b/client/src/app/instance/search/containers/result.component.ts @@ -12,19 +12,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -import { Criterion, SearchQueryParams, Pagination } from '../../store/models'; -import { Dataset, CriteriaFamily, OutputFamily, Attribute, OutputCategory } from 'src/app/metamodel/models'; -import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; -import * as datasetActions from 'src/app/metamodel/actions/dataset.actions'; -import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; -import * as attributeActions from 'src/app/metamodel/actions/attribute.actions'; -import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector'; -import * as criteriaFamilyActions from 'src/app/metamodel/actions/criteria-family.actions'; -import * as criteriaFamilySelector from 'src/app/metamodel/selectors/criteria-family.selector'; -import * as outputFamilyActions from 'src/app/metamodel/actions/output-family.actions'; -import * as outputFamilySelector from 'src/app/metamodel/selectors/output-family.selector'; -import * as outputCategoryActions from 'src/app/metamodel/actions/output-category.actions'; -import * as outputCategorySelector from 'src/app/metamodel/selectors/output-category.selector'; +import { AbstractSearchComponent } from './abstract-search.component'; +import { Pagination } from '../../store/models'; import * as searchActions from '../../store/actions/search.actions'; import * as searchSelector from '../../store/selectors/search.selector'; import * as sampActions from '../../store/actions/samp.actions'; @@ -41,55 +30,14 @@ import * as sampSelector from '../../store/selectors/samp.selector'; * @implements OnInit * @implements OnDestroy */ -export class ResultComponent implements OnInit, OnDestroy { - public datasetSelected: Observable<string>; - public instanceSelected: Observable<string>; - public datasetListIsLoading: Observable<boolean>; - public datasetListIsLoaded: Observable<boolean>; - public datasetList: Observable<Dataset[]>; - public attributeList: Observable<Attribute[]>; - public attributeListIsLoading: Observable<boolean>; - public attributeListIsLoaded: Observable<boolean>; - public criteriaFamilyList: Observable<CriteriaFamily[]>; - public criteriaFamilyListIsLoading: Observable<boolean>; - public criteriaFamilyListIsLoaded: Observable<boolean>; - public outputFamilyList: Observable<OutputFamily[]>; - public outputFamilyListIsLoading: Observable<boolean>; - public outputFamilyListIsLoaded: Observable<boolean>; - public outputCategoryList: Observable<OutputCategory[]>; - public outputCategoryListIsLoading: Observable<boolean>; - public outputCategoryListIsLoaded: Observable<boolean>; - public currentStep: Observable<string>; - public criteriaList: Observable<Criterion[]>; - public outputList: Observable<number[]>; - public queryParams: Observable<SearchQueryParams>; +export class ResultComponent extends AbstractSearchComponent { public dataLength: Observable<number>; public dataLengthIsLoading: Observable<boolean>; public dataLengthIsLoaded: Observable<boolean>; public sampRegistered: Observable<boolean>; - constructor(private store: Store<{ }>) { - this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute); - this.instanceSelected = store.select(instanceSelector.selectInstanceNameByRoute); - this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); - this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); - this.datasetList = store.select(datasetSelector.selectAllDatasets); - this.attributeList = store.select(attributeSelector.selectAllAttributes); - this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading); - this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded); - this.criteriaFamilyList = store.select(criteriaFamilySelector.selectAllCriteriaFamilies); - this.criteriaFamilyListIsLoading = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoading); - this.criteriaFamilyListIsLoaded = store.select(criteriaFamilySelector.selectCriteriaFamilyListIsLoaded); - this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilies); - this.outputFamilyListIsLoading = store.select(outputFamilySelector.selectOutputFamilyListIsLoading); - this.outputFamilyListIsLoaded = store.select(outputFamilySelector.selectOutputFamilyListIsLoaded); - this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategories); - this.outputCategoryListIsLoading = store.select(outputCategorySelector.selectOutputCategoryListIsLoading); - this.outputCategoryListIsLoaded = store.select(outputCategorySelector.selectOutputCategoryListIsLoaded); - this.currentStep = this.store.select(searchSelector.selectCurrentStep); - this.criteriaList = this.store.select(searchSelector.selectCriteriaList); - this.outputList = this.store.select(searchSelector.selectOutputList); - this.queryParams = this.store.select(searchSelector.selectQueryParams); + constructor(protected store: Store<{ }>) { + super(store); this.dataLength = this.store.select(searchSelector.selectDataLength); this.dataLengthIsLoading = this.store.select(searchSelector.selectDataLengthIsLoading); this.dataLengthIsLoaded = this.store.select(searchSelector.selectDataLengthIsLoaded); @@ -101,16 +49,7 @@ export class ResultComponent implements OnInit, OnDestroy { // This micro task prevent the expression has changed after view init error Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'result' }))); Promise.resolve(null).then(() => this.store.dispatch(searchActions.checkResult())); - - this.store.dispatch(datasetActions.loadDatasetList()); - this.datasetSelected.subscribe(dname => { - if (dname) { - this.store.dispatch(attributeActions.loadAttributeList()); - this.store.dispatch(criteriaFamilyActions.loadCriteriaFamilyList()); - this.store.dispatch(outputFamilyActions.loadOutputFamilyList()); - this.store.dispatch(outputCategoryActions.loadOutputCategoryList()); - } - }); + super.ngOnInit(); } sampRegister() { @@ -164,5 +103,6 @@ export class ResultComponent implements OnInit, OnDestroy { */ ngOnDestroy() { this.store.dispatch(searchActions.destroyResults()); + super.ngOnDestroy(); } } diff --git a/client/src/app/instance/search/search.component.ts b/client/src/app/instance/search/search.component.ts index ac08afa7..175c4e9b 100644 --- a/client/src/app/instance/search/search.component.ts +++ b/client/src/app/instance/search/search.component.ts @@ -44,7 +44,5 @@ export class SearchComponent { this.resultStepChecked = store.select(searchSelector.selectResultStepChecked); this.queryParams = this.store.select(searchSelector.selectQueryParams); this.outputList = this.store.select(searchSelector.selectOutputList); - // this.store.dispatch(new searchMultipleActions.ResetSearchAction()); - // this.store.dispatch(new coneSearchActions.DeleteConeSearchAction()); } } diff --git a/client/src/app/instance/store/actions/metamodel.actions.ts b/client/src/app/instance/store/actions/metamodel.actions.ts new file mode 100644 index 00000000..e7e7638b --- /dev/null +++ b/client/src/app/instance/store/actions/metamodel.actions.ts @@ -0,0 +1,13 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { createAction, props } from '@ngrx/store'; + +export const loadInstanceMetamodel = createAction('[Metamodel] Load Instance Metamodel'); +export const changeCurrentInstance = createAction('[Metamodel] Change Current Instance', props<{ currentInstance: string }>()); diff --git a/client/src/app/instance/store/actions/search.actions.ts b/client/src/app/instance/store/actions/search.actions.ts index e8ff7dd3..d180d538 100644 --- a/client/src/app/instance/store/actions/search.actions.ts +++ b/client/src/app/instance/store/actions/search.actions.ts @@ -12,6 +12,11 @@ import { createAction, props } from '@ngrx/store'; import { Criterion, Pagination } from '../models'; export const initSearch = createAction('[Search] Init Search'); +export const restartSearch = createAction('[Search] Restart Search'); +export const loadDefaultCriteriaList = createAction('[Search] Load Default Criteria List'); +export const loadDefaultOutputList = createAction('[Search] Load Default Output List'); +export const markAsDirty = createAction('[Search] Mark As Dirty'); +export const changeCurrentDataset = createAction('[Search] Change Current Dataset', props<{ currentDataset: string }>()); export const changeStep = createAction('[Search] Change Step', props<{ step: string }>()); export const checkCriteria = createAction('[Search] Check Criteria'); export const checkOutput = createAction('[Search] Check Output'); diff --git a/client/src/app/instance/store/effects/index.ts b/client/src/app/instance/store/effects/index.ts index 806a9ba4..296d89b2 100644 --- a/client/src/app/instance/store/effects/index.ts +++ b/client/src/app/instance/store/effects/index.ts @@ -1,7 +1,9 @@ +import { MetamodelEffects } from './metamodel.effects'; import { SampEffects } from "./samp.effects"; import { SearchEffects } from "./search.effects"; export const instanceEffects = [ + MetamodelEffects, SampEffects, SearchEffects ]; diff --git a/client/src/app/instance/store/effects/metamodel.effects.ts b/client/src/app/instance/store/effects/metamodel.effects.ts new file mode 100644 index 00000000..c81b9168 --- /dev/null +++ b/client/src/app/instance/store/effects/metamodel.effects.ts @@ -0,0 +1,57 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Injectable } from '@angular/core'; + +import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; +import { Store } from '@ngrx/store'; +import { of } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +import * as metamodelActions from '../actions/metamodel.actions'; +import * as metamodelSelector from '../selectors/metamodel.selector'; +import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; +import * as datasetActions from 'src/app/metamodel/actions/dataset.actions'; +import * as datasetFamilyActions from 'src/app/metamodel/actions/dataset-family.actions'; +import * as surveyActions from 'src/app/metamodel/actions/survey.actions'; + +@Injectable() +export class MetamodelEffects { + loadInstanceMetamodel$ = createEffect(() => + this.actions$.pipe( + ofType(metamodelActions.loadInstanceMetamodel), + concatLatestFrom(() => [ + this.store.select(instanceSelector.selectInstanceNameByRoute), + this.store.select(metamodelSelector.selectCurrentInstance), + this.store.select(metamodelSelector.selectInstanceMetamodelIsLoaded) + ]), + mergeMap(([action, instanceName, currentInstance, instanceMetamodelIsLoaded]) => { + if (instanceName && instanceName !== currentInstance) { + instanceMetamodelIsLoaded = false; + } + + if (!instanceMetamodelIsLoaded) { + return [ + metamodelActions.changeCurrentInstance({ currentInstance: instanceName }), + datasetFamilyActions.loadDatasetFamilyList(), + datasetActions.loadDatasetList(), + surveyActions.loadSurveyList() + ]; + } else { + return of({ type: '[No Action] Load Instance Metamodel' }); + } + }) + ) + ); + + constructor( + private actions$: Actions, + private store: Store<{ }> + ) {} +} diff --git a/client/src/app/instance/store/effects/search.effects.ts b/client/src/app/instance/store/effects/search.effects.ts index bd974095..f400e805 100644 --- a/client/src/app/instance/store/effects/search.effects.ts +++ b/client/src/app/instance/store/effects/search.effects.ts @@ -7,22 +7,187 @@ * file that was distributed with this source code. */ - import { Injectable } from '@angular/core'; - - import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; - import { Store } from '@ngrx/store'; - import { of } from 'rxjs'; - import { map, tap, mergeMap, catchError } from 'rxjs/operators'; - import { ToastrService } from 'ngx-toastr'; - - import { getCriterionStr } from '../models'; - import { SearchService } from '../services/search.service'; - import * as searchActions from '../actions/search.actions'; - import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; - import * as searchSelector from '../selectors/search.selector'; - - @Injectable() - export class SearchEffects { +import { Injectable } from '@angular/core'; + +import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; +import { Store, Action } from '@ngrx/store'; +import { of } from 'rxjs'; +import { map, tap, mergeMap, catchError } from 'rxjs/operators'; +import { ToastrService } from 'ngx-toastr'; + +import { getCriterionStr } from '../models'; +import { SearchService } from '../services/search.service'; +import * as searchActions from '../actions/search.actions'; +import * as attributeActions from 'src/app/metamodel/actions/attribute.actions'; +import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector'; +import * as criteriaFamilyActions from 'src/app/metamodel/actions/criteria-family.actions'; +import * as outputFamilyActions from 'src/app/metamodel/actions/output-family.actions'; +import * as outputCategoryActions from 'src/app/metamodel/actions/output-category.actions'; +import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; +import * as searchSelector from '../selectors/search.selector'; + +@Injectable() +export class SearchEffects { + initSearch$ = createEffect(() => + this.actions$.pipe( + ofType(searchActions.initSearch), + concatLatestFrom(() => [ + this.store.select(datasetSelector.selectDatasetNameByRoute), + this.store.select(searchSelector.selectCurrentDataset), + this.store.select(searchSelector.selectPristine), + this.store.select(searchSelector.selectStepsByRoute) + ]), + mergeMap(([action, datasetName, currentDataset, pristine, steps]) => { + // User has changed dataset: reload initial state + init search + if (datasetName && currentDataset && datasetName !== currentDataset) { + return of(searchActions.restartSearch()); + } + + // User has selected a dataset or page is reloaded: load dataset metamodel + if (datasetName && pristine) { + let actions: Action[] = [ + searchActions.markAsDirty(), + searchActions.changeCurrentDataset({ currentDataset: datasetName }), + attributeActions.loadAttributeList(), + criteriaFamilyActions.loadCriteriaFamilyList(), + outputFamilyActions.loadOutputFamilyList(), + outputCategoryActions.loadOutputCategoryList() + ]; + if (steps) { + if(steps[0] === '1') { + actions.push(searchActions.checkCriteria()); + } + if(steps[1] === '1') { + actions.push(searchActions.checkOutput()); + } + if(steps[2] === '1') { + actions.push(searchActions.checkResult()); + } + } + return actions; + } + + // User come back to the search module: reload initial state + if(!datasetName && !pristine) { + return of(searchActions.resetSearch()); + } + + // User change step and it's the same search: No action + return of({ type: '[No Action] Init Search' }); + }) + ) + ); + + restartSearch$ = createEffect(() => + this.actions$.pipe( + ofType(searchActions.restartSearch), + map(() => searchActions.initSearch()) + ) + ); + + loadDefaultCriteriaList$ = createEffect(() => + this.actions$.pipe( + ofType(searchActions.loadDefaultCriteriaList), + concatLatestFrom(() => [ + this.store.select(attributeSelector.selectAllAttributes), + this.store.select(searchSelector.selectCriteriaListByRoute) + ]), + mergeMap(([action, attributeList, criteriaList]) => { + let defaultCriteriaList = []; + if (criteriaList) { + defaultCriteriaList = criteriaList.split(';').map((c: string) => { + const params = c.split('::'); + const attribute = attributeList.find(a => a.id === parseInt(params[0], 10)); + switch (attribute.search_type) { + case 'field': + case 'select': + case 'datalist': + case 'radio': + case 'date': + case 'date-time': + case 'time': + return { id: parseInt(params[0], 10), type: 'field', operator: params[1], value: params[2] }; + case 'list': + return { id: parseInt(params[0], 10), type: 'list', values: params[2].split('|') }; + case 'between': + case 'between-date': + if (params[1] === 'bw') { + const bwValues = params[2].split('|'); + return { id: parseInt(params[0], 10), type: 'between', min: bwValues[0], max: bwValues[1] }; + } else if (params[1] === 'gte') { + return { id: parseInt(params[0], 10), type: 'between', min: params[2], max: null }; + } else { + return { id: parseInt(params[0], 10), type: 'between', min: null, max: params[2] }; + } + case 'select-multiple': + case 'checkbox': + const msValues = params[2].split('|'); + const options = attribute.options.filter(option => msValues.includes(option.value)); + return { id: parseInt(params[0], 10), type: 'multiple', options }; + case 'json': + const [path, operator, value] = params[2].split('|'); + return { id: parseInt(params[0], 10), type: 'json', path, operator, value }; + default: + return null; + } + }); + } else { + defaultCriteriaList = attributeList + .filter(attribute => attribute.id_criteria_family && attribute.search_type && attribute.min) + .map(attribute => { + switch (attribute.search_type) { + case 'field': + case 'select': + case 'datalist': + case 'radio': + case 'date': + case 'date-time': + case 'time': + return { id: attribute.id, type: 'field', value: attribute.min.toString(), operator: attribute.operator }; + case 'list': + return { id: attribute.id, type: 'list', values: attribute.min.toString().split('|') }; + case 'between': + case 'between-date': + return { id: attribute.id, type: 'between', min: attribute.min.toString(), max: attribute.max.toString() }; + case 'select-multiple': + case 'checkbox': + const msValues = attribute.min.toString().split('|'); + const options = attribute.options.filter(option => msValues.includes(option.value)); + return { id: attribute.id, type: 'multiple', options }; + case 'json': + const [path, operator, value] = attribute.min.toString().split('|'); + return { id: attribute.id, type: 'json', path, operator, value }; + default: + return null; + } + }); + } + return of(searchActions.updateCriteriaList({ criteriaList: defaultCriteriaList })); + }) + ) + ); + + loadDefaultOutputList$ = createEffect(() => + this.actions$.pipe( + ofType(searchActions.loadDefaultOutputList), + concatLatestFrom(() => [ + this.store.select(attributeSelector.selectAllAttributes), + this.store.select(searchSelector.selectOutputListByRoute) + ]), + mergeMap(([action, attributeList, outputList]) => { + let defaultOutputList = []; + if (outputList) { + defaultOutputList = outputList.split(';').map((o: string) => parseInt(o, 10)); + } else { + defaultOutputList = attributeList + .filter(attribute => attribute.selected && attribute.id_output_category) + .map(attribute => attribute.id); + } + return of(searchActions.updateOutputList({ outputList: defaultOutputList })); + }) + ) + ); + retrieveDataLength$ = createEffect(() => this.actions$.pipe( ofType(searchActions.retrieveDataLength), @@ -90,4 +255,4 @@ private store: Store<{ }>, private toastr: ToastrService ) {} - } +} diff --git a/client/src/app/instance/store/reducers/metamodel.reducer.ts b/client/src/app/instance/store/reducers/metamodel.reducer.ts new file mode 100644 index 00000000..6a1733f0 --- /dev/null +++ b/client/src/app/instance/store/reducers/metamodel.reducer.ts @@ -0,0 +1,34 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { createReducer, on } from '@ngrx/store'; + +import * as metamodelActions from '../actions/metamodel.actions'; + +export interface State { + currentInstance: string; + instanceMetamodelIsLoaded: boolean; +} + +export const initialState: State = { + currentInstance: null, + instanceMetamodelIsLoaded: false +} + +export const metamodelReducer = createReducer( + initialState, + on(metamodelActions.changeCurrentInstance, (state, { currentInstance }) => ({ + ...state, + currentInstance, + instanceMetamodelIsLoaded: true + })) +); + +export const selectCurrentInstance = (state: State) => state.currentInstance; +export const selectInstanceMetamodelIsLoaded = (state: State) => state.instanceMetamodelIsLoaded; diff --git a/client/src/app/instance/store/reducers/search.reducer.ts b/client/src/app/instance/store/reducers/search.reducer.ts index 96608d3b..94650279 100644 --- a/client/src/app/instance/store/reducers/search.reducer.ts +++ b/client/src/app/instance/store/reducers/search.reducer.ts @@ -14,6 +14,7 @@ import * as searchActions from '../actions/search.actions'; export interface State { pristine: boolean; + currentDataset: string, currentStep: string; criteriaStepChecked: boolean; outputStepChecked: boolean; @@ -30,6 +31,7 @@ export interface State { export const initialState: State = { pristine: true, + currentDataset: null, currentStep: null, criteriaStepChecked: false, outputStepChecked: false, @@ -46,10 +48,22 @@ export const initialState: State = { export const searchReducer = createReducer( initialState, + on(searchActions.restartSearch, () => ({ + ...initialState, + currentStep: 'dataset' + })), on(searchActions.changeStep, (state, { step }) => ({ ...state, currentStep: step })), + on(searchActions.markAsDirty, state => ({ + ...state, + pristine: false + })), + on(searchActions.changeCurrentDataset, (state, { currentDataset }) => ({ + ...state, + currentDataset + })), on(searchActions.checkCriteria, state => ({ ...state, criteriaStepChecked: true @@ -97,6 +111,7 @@ export const searchReducer = createReducer( ); export const selectPristine = (state: State) => state.pristine; +export const selectCurrentDataset = (state: State) => state.currentDataset; export const selectCurrentStep = (state: State) => state.currentStep; export const selectCriteriaStepChecked = (state: State) => state.criteriaStepChecked; export const selectOutputStepChecked = (state: State) => state.outputStepChecked; diff --git a/client/src/app/instance/store/selectors/metamodel.selector.ts b/client/src/app/instance/store/selectors/metamodel.selector.ts new file mode 100644 index 00000000..31195a60 --- /dev/null +++ b/client/src/app/instance/store/selectors/metamodel.selector.ts @@ -0,0 +1,28 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { createSelector } from '@ngrx/store'; + +import * as reducer from '../../instance.reducer'; +import * as fromMetamodel from '../reducers/metamodel.reducer'; + +export const selectMetamodelState = createSelector( + reducer.getInstanceState, + (state: reducer.State) => state.metamodel +); + +export const selectCurrentInstance = createSelector( + selectMetamodelState, + fromMetamodel.selectCurrentInstance +); + +export const selectInstanceMetamodelIsLoaded = createSelector( + selectMetamodelState, + fromMetamodel.selectInstanceMetamodelIsLoaded +); diff --git a/client/src/app/instance/store/selectors/samp.selector.ts b/client/src/app/instance/store/selectors/samp.selector.ts index 3446fc74..d485b19f 100644 --- a/client/src/app/instance/store/selectors/samp.selector.ts +++ b/client/src/app/instance/store/selectors/samp.selector.ts @@ -7,11 +7,15 @@ * file that was distributed with this source code. */ -import { createSelector, createFeatureSelector } from '@ngrx/store'; +import { createSelector } from '@ngrx/store'; +import * as reducer from '../../instance.reducer'; import * as fromSamp from '../reducers/samp.reducer'; -export const selectSampState = createFeatureSelector<fromSamp.State>('samp'); +export const selectSampState = createSelector( + reducer.getInstanceState, + (state: reducer.State) => state.samp +); export const selectRegistered = createSelector( selectSampState, diff --git a/client/src/app/instance/store/selectors/search.selector.ts b/client/src/app/instance/store/selectors/search.selector.ts index e7a8233c..af427be8 100644 --- a/client/src/app/instance/store/selectors/search.selector.ts +++ b/client/src/app/instance/store/selectors/search.selector.ts @@ -13,73 +13,78 @@ import { Criterion, SearchQueryParams, getCriterionStr } from '../models'; import * as reducer from '../../instance.reducer'; import * as fromSearch from '../reducers/search.reducer'; -export const selectAttributeState = createSelector( +export const selectInstanceState = createSelector( reducer.getInstanceState, (state: reducer.State) => state.search ); export const selectPristine = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectPristine ); +export const selectCurrentDataset = createSelector( + selectInstanceState, + fromSearch.selectCurrentDataset +); + export const selectCurrentStep = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectCurrentStep ); export const selectCriteriaStepChecked = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectCriteriaStepChecked ); export const selectOutputStepChecked = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectOutputStepChecked ); export const selectResultStepChecked = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectResultStepChecked ); export const selectIsConeSearchAdded = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectIsConeSearchAdded ); export const selectCriteriaList = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectCriteriaList ); export const selectOutputList = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectOutputList ); export const selectSearchData = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectSearchData ); export const selectDataLengthIsLoading = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectDataLengthIsLoading ); export const selectDataLengthIsLoaded = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectDataLengthIsLoaded ); export const selectDataLength = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectDataLength ); export const selectSelectedData = createSelector( - selectAttributeState, + selectInstanceState, fromSearch.selectSelectedData ); @@ -112,3 +117,18 @@ export const selectQueryParams = createSelector( return queryParams; } ); + +export const selectStepsByRoute = createSelector( + reducer.selectRouterState, + router => router.state.queryParams.s as string +); + +export const selectCriteriaListByRoute = createSelector( + reducer.selectRouterState, + router => router.state.queryParams.c as string +); + +export const selectOutputListByRoute = createSelector( + reducer.selectRouterState, + router => router.state.queryParams.a as string +); diff --git a/client/src/app/metamodel/reducers/attribute.reducer.ts b/client/src/app/metamodel/reducers/attribute.reducer.ts index 6db9742e..9276122e 100644 --- a/client/src/app/metamodel/reducers/attribute.reducer.ts +++ b/client/src/app/metamodel/reducers/attribute.reducer.ts @@ -30,7 +30,8 @@ export const attributeReducer = createReducer( on(attributeActions.loadAttributeList, (state) => { return { ...state, - attributeListIsLoading: true + attributeListIsLoading: true, + attributeListIsLoaded: false } }), on(attributeActions.loadAttributeListSuccess, (state, { attributes }) => { -- GitLab