Move comparer and extractor functions into state value classes

This clears up what the controller has to do and allows us to double
check

1. The previous value of the state has changed
2. IF (1) is true, whether the query value for that attribute is changed

We need to check the second part additionally since the states may be
updated as a result of the query being updated in
`wpListService.updateStatesFromQuery`

The 'correct' solution here would be to create two input states, and a
dependent state that executes resulting actions.
pull/5397/head
Oliver Günther 8 years ago
parent 4aece22d52
commit 90c6fdfed6
No known key found for this signature in database
GPG Key ID: 88872239EB414F99
  1. 103
      frontend/app/components/routing/wp-list/wp-list.controller.ts
  2. 6
      frontend/app/components/wp-fast-table/state/wp-table-base.service.ts
  3. 2
      frontend/app/components/wp-fast-table/state/wp-table-columns.service.ts
  4. 2
      frontend/app/components/wp-fast-table/state/wp-table-filters.service.ts
  5. 2
      frontend/app/components/wp-fast-table/state/wp-table-pagination.service.ts
  6. 20
      frontend/app/components/wp-fast-table/wp-table-base.ts
  7. 4
      frontend/app/components/wp-fast-table/wp-table-columns.ts
  8. 8
      frontend/app/components/wp-fast-table/wp-table-filters.ts
  9. 8
      frontend/app/components/wp-fast-table/wp-table-group-by.ts
  10. 4
      frontend/app/components/wp-fast-table/wp-table-hierarchies.ts
  11. 4
      frontend/app/components/wp-fast-table/wp-table-pagination.ts
  12. 9
      frontend/app/components/wp-fast-table/wp-table-sort-by.ts
  13. 4
      frontend/app/components/wp-fast-table/wp-table-sum.ts
  14. 4
      frontend/app/components/wp-fast-table/wp-table-timeline-visible.ts

@ -43,13 +43,9 @@ import {WorkPackagesListService} from "../../wp-list/wp-list.service";
import {WorkPackageTableTimelineService} from "../../wp-fast-table/state/wp-table-timeline.service"; import {WorkPackageTableTimelineService} from "../../wp-fast-table/state/wp-table-timeline.service";
import {WorkPackageTableBaseService} from "../../wp-fast-table/state/wp-table-base.service"; import {WorkPackageTableBaseService} from "../../wp-fast-table/state/wp-table-base.service";
import {HalResource} from "../../api/api-v3/hal-resources/hal-resource.service"; import {HalResource} from "../../api/api-v3/hal-resources/hal-resource.service";
import {WorkPackageTableBaseState} from "../../wp-fast-table/wp-table-base";
interface ObserverSetupOptions {
attrName:string;
keyFunc?:(a:any) => any;
triggerUpdate?:boolean;
halMap?:boolean;
}
function WorkPackagesListController($scope:any, function WorkPackagesListController($scope:any,
$rootScope:ng.IRootScopeService, $rootScope:ng.IRootScopeService,
@ -109,77 +105,50 @@ function WorkPackagesListController($scope:any,
} }
}); });
setupChangeObserver(wpTableSortBy, { attrName: 'sortBy', triggerUpdate: true, halMap: true }); setupChangeObserver(wpTableFilters, 'filters');
setupChangeObserver(wpTableSum, { attrName: 'sums', triggerUpdate: true }); setupChangeObserver(wpTableGroupBy, 'groupBy');
setupChangeObserver(wpTableTimeline, { attrName: 'timelineVisible', triggerUpdate: false }); setupChangeObserver(wpTableSortBy, 'sortBy');
setupChangeObserver(wpTableHierarchies, { attrName: 'showHierarchies', triggerUpdate: false }); setupChangeObserver(wpTableSum, 'sums');
setupChangeObserver(wpTableColumns, { attrName: 'columns', triggerUpdate: false, halMap: true }); setupChangeObserver(wpTableTimeline, 'timelineVisible', false);
setupChangeObserver(wpTableHierarchies, 'showHierarchies', false);
// Filter objects do not have hrefs to compare them by, but we can use a copy of their source setupChangeObserver(wpTableColumns, 'columns', false);
// (copying is necessary while the state objects are still mutable)
setupChangeObserver(
wpTableFilters,
{
attrName: 'filters',
triggerUpdate: true,
keyFunc: (stateValue: any) => stateValue.current && stateValue.current.map((el:HalResource) => el.$plain())
}
);
// Group by is a single HAL resource instead of an array
setupChangeObserver(
wpTableGroupBy,
{
attrName: 'groupBy',
triggerUpdate: true,
keyFunc: (stateValue: any) => stateValue.current && stateValue.current.href
}
);
} }
function setupChangeObserver(service:WorkPackageTableBaseService, options:ObserverSetupOptions) { function setupChangeObserver(service:WorkPackageTableBaseService, name:string, triggerUpdate:boolean = true) {
let keyFunc;
if (options.keyFunc) {
keyFunc = options.keyFunc;
} else if (options.halMap) {
keyFunc = (stateValue:any) => stateValue.current && stateValue.current.map((el:HalResource) => el.href);
} else {
keyFunc = (stateValue:any) => stateValue.current;
}
service service
.observeUntil(scopeDestroyed$($scope)) .observeUntil(scopeDestroyed$($scope))
.distinctUntilChanged( .distinctUntilChanged(
(a,b) => _.isEqual(a,b), (a,b) => _.isEqual(a,b),
keyFunc (stateValue:WorkPackageTableBaseState<any>) => stateValue.extractedCompareValue
) )
.skip(1) // Skip the first value to avoid distinctUntilChanged firing for empty .subscribe((stateValue:WorkPackageTableBaseState<any>) => {
.subscribe((stateValue:any) => { // Avoid updating while not all states are initialized
updateAndExecuteIfAltered(stateValue.current, options.attrName, options.triggerUpdate); if (isAnyDependentStateClear()) {
return;
}
// Avoid updating if the query isn't yet available
const query = states.table.query.value;
if (!query) {
return;
}
// Avoid updating if the query is up-to-date
// (Loaded into query elsewhere)
const queryValue = stateValue.comparerFunction()(query[name]);
if (_.isEqual(queryValue, stateValue.extractedCompareValue)) {
return;
}
query[name] = _.cloneDeep(stateValue.current);
states.table.query.putValue(query);
if (triggerUpdate) {
updateResultsVisibly(true);
}
}); });
} }
function updateAndExecuteIfAltered(updateData:any, name:string, triggerUpdate:boolean = false) {
if (isAnyDependentStateClear()) {
return;
}
let query = states.table.query.value;
if (!query) {
return;
}
query[name] = _.cloneDeep(updateData);
states.table.query.putValue(query);
if (triggerUpdate) {
updateResultsVisibly(true);
}
}
function loadQuery() { function loadQuery() {
wpListChecksumService.clear(); wpListChecksumService.clear();
loadingIndicator.table.promise = wpListService.fromQueryParams($state.params, $scope.projectIdentifier); loadingIndicator.table.promise = wpListService.fromQueryParams($state.params, $scope.projectIdentifier);

@ -28,7 +28,7 @@
import {InputState} from "reactivestates"; import {InputState} from "reactivestates";
import {States} from "../../states.service"; import {States} from "../../states.service";
import {WorkPackageTableBaseInterface} from "../wp-table-base"; import {WorkPackageTableBaseState} from "../wp-table-base";
import {scopedObservable} from "../../../helpers/angular-rx-utils"; import {scopedObservable} from "../../../helpers/angular-rx-utils";
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
@ -46,10 +46,12 @@ export abstract class WorkPackageTableBaseService {
constructor(protected states: States) { constructor(protected states: States) {
} }
protected get state(): InputState<WorkPackageTableBaseInterface> { protected get state(): InputState<any> {
return this.states.table[this.stateName]; return this.states.table[this.stateName];
}; };
public observeOnScope(scope:ng.IScope) { public observeOnScope(scope:ng.IScope) {
return scopedObservable(scope, this.state.values$()); return scopedObservable(scope, this.state.values$());
} }

@ -38,7 +38,7 @@ import {WPTableRowSelectionState} from '../wp-table.interfaces';
import {QueryColumn} from '../../api/api-v3/hal-resources/query-resource.service' import {QueryColumn} from '../../api/api-v3/hal-resources/query-resource.service'
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {WorkPackageTableColumns} from '../wp-table-columns' import {WorkPackageTableColumns} from '../wp-table-columns'
import {WorkPackageTableBaseInterface} from '../wp-table-base'; import {WorkPackageTableBaseState} from '../wp-table-base';
import {QueryResource} from '../../api/api-v3/hal-resources/query-resource.service'; import {QueryResource} from '../../api/api-v3/hal-resources/query-resource.service';
import {QuerySchemaResourceInterface} from '../../api/api-v3/hal-resources/query-schema-resource.service'; import {QuerySchemaResourceInterface} from '../../api/api-v3/hal-resources/query-schema-resource.service';

@ -40,6 +40,8 @@ import {CollectionResource} from '../../api/api-v3/hal-resources/collection-reso
import {opServicesModule} from '../../../angular-modules'; import {opServicesModule} from '../../../angular-modules';
import {States} from '../../states.service'; import {States} from '../../states.service';
import {WorkPackageTableFilters} from '../wp-table-filters'; import {WorkPackageTableFilters} from '../wp-table-filters';
import {WorkPackageTableBaseState} from "../wp-table-base";
import {HalResource} from "../../api/api-v3/hal-resources/hal-resource.service";
export class WorkPackageTableFiltersService extends WorkPackageTableBaseService { export class WorkPackageTableFiltersService extends WorkPackageTableBaseService {
protected stateName = 'filters' as TableStateStates; protected stateName = 'filters' as TableStateStates;

@ -38,7 +38,7 @@ import {
} from '../../api/api-v3/hal-resources/query-resource.service'; } from '../../api/api-v3/hal-resources/query-resource.service';
import {WorkPackageTablePagination} from '../wp-table-pagination'; import {WorkPackageTablePagination} from '../wp-table-pagination';
import { import {
WorkPackageTableBaseInterface, WorkPackageTableBaseState,
} from '../wp-table-base'; } from '../wp-table-base';
interface PaginationUpdateObject { interface PaginationUpdateObject {

@ -26,6 +26,22 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
// ++ // ++
export interface WorkPackageTableBaseInterface { export class WorkPackageTableBaseState<T> {
current:any
/**
* Extract the current value from this state and pass it through the comparer function.
*/
public get extractedCompareValue():any {
return this.current && this.comparerFunction()(this.current);
}
/**
* Returns a comparer function for this state's value used to compare state values,
* e.g., as a distinctUntilChanged() key function.
*/
public comparerFunction():(current:T) => any {
return (current: T) => current;
}
public current:T;
} }

@ -30,8 +30,9 @@ import {QueryColumn} from '../api/api-v3/hal-resources/query-resource.service'
import {QueryResource} from '../api/api-v3/hal-resources/query-resource.service' import {QueryResource} from '../api/api-v3/hal-resources/query-resource.service'
import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service' import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service'
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableColumns { export class WorkPackageTableColumns extends WorkPackageTableBaseState<QueryColumn[]> {
// Available columns // Available columns
public available:QueryColumn[]|undefined; public available:QueryColumn[]|undefined;
@ -40,6 +41,7 @@ export class WorkPackageTableColumns {
public current:QueryColumn[]; public current:QueryColumn[];
constructor(query:QueryResource, schema?:QuerySchemaResourceInterface) { constructor(query:QueryResource, schema?:QuerySchemaResourceInterface) {
super();
this.update(query, schema); this.update(query, schema);
} }

@ -36,12 +36,18 @@ import {QueryResource} from '../api/api-v3/hal-resources/query-resource.service'
import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service'; import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service';
import {QueryFilterInstanceSchemaResource} from '../api/api-v3/hal-resources/query-filter-instance-schema-resource.service'; import {QueryFilterInstanceSchemaResource} from '../api/api-v3/hal-resources/query-filter-instance-schema-resource.service';
import {HalResource} from "../api/api-v3/hal-resources/hal-resource.service"; import {HalResource} from "../api/api-v3/hal-resources/hal-resource.service";
import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableFilters { export class WorkPackageTableFilters extends WorkPackageTableBaseState<QueryFilterInstanceResource[]> {
public availableSchemas:QueryFilterInstanceSchemaResource[] = []; public availableSchemas:QueryFilterInstanceSchemaResource[] = [];
public current:QueryFilterInstanceResource[] = []; public current:QueryFilterInstanceResource[] = [];
public comparerFunction():(current:QueryFilterInstanceResource[]) => any {
return (current:QueryFilterInstanceResource[]) => current.map((el:HalResource) => el.$plain());
}
constructor(filters:QueryFilterInstanceResource[], schema:QuerySchemaResourceInterface) { constructor(filters:QueryFilterInstanceResource[], schema:QuerySchemaResourceInterface) {
super();
this.current = filters; this.current = filters;
this.availableSchemas = schema this.availableSchemas = schema
.filtersSchemas .filtersSchemas

@ -32,12 +32,18 @@ import {
QueryColumn QueryColumn
} from '../api/api-v3/hal-resources/query-resource.service'; } from '../api/api-v3/hal-resources/query-resource.service';
import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service'; import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service';
import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableGroupBy { export class WorkPackageTableGroupBy extends WorkPackageTableBaseState<QueryGroupByResource | undefined> {
public available:QueryGroupByResource[] = []; public available:QueryGroupByResource[] = [];
public current:QueryGroupByResource | undefined; public current:QueryGroupByResource | undefined;
public comparerFunction():(current:QueryGroupByResource|undefined) => any {
return (current:QueryGroupByResource) => current && current.href;
}
constructor(query:QueryResource, schema?:QuerySchemaResourceInterface) { constructor(query:QueryResource, schema?:QuerySchemaResourceInterface) {
super();
this.current = angular.copy(query.groupBy); this.current = angular.copy(query.groupBy);
if (schema) { if (schema) {

@ -26,11 +26,13 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
// ++ // ++
export class WorkPackageTableHierarchies { import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableHierarchies extends WorkPackageTableBaseState<boolean> {
public current:boolean; public current:boolean;
public collapsed:{[workPackageId:string]:boolean}; public collapsed:{[workPackageId:string]:boolean};
constructor(isVisible:boolean) { constructor(isVisible:boolean) {
super();
this.current = isVisible; this.current = isVisible;
this.collapsed = {}; this.collapsed = {};
} }

@ -27,12 +27,14 @@
// ++ // ++
import {WorkPackageCollectionResource} from '../api/api-v3/hal-resources/wp-collection-resource.service' import {WorkPackageCollectionResource} from '../api/api-v3/hal-resources/wp-collection-resource.service'
import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTablePaginationObject { export class WorkPackageTablePaginationObject extends WorkPackageTableBaseState<WorkPackageTablePagination> {
constructor(public page:number, constructor(public page:number,
public perPage:number, public perPage:number,
public total:number, public total:number,
public count:number) { public count:number) {
super();
} }
} }

@ -36,16 +36,23 @@ import {
QueryColumn QueryColumn
} from '../api/api-v3/hal-resources/query-resource.service'; } from '../api/api-v3/hal-resources/query-resource.service';
import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service'; import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service';
import {WorkPackageTableBaseState} from "./wp-table-base";
import {HalResource} from "../api/api-v3/hal-resources/hal-resource.service";
export class WorkPackageTableSortBy { export class WorkPackageTableSortBy extends WorkPackageTableBaseState<QuerySortByResource[]> {
public available: QuerySortByResource[] = []; public available: QuerySortByResource[] = [];
public current:QuerySortByResource[] = []; public current:QuerySortByResource[] = [];
constructor(query:QueryResource, schema:QuerySchemaResourceInterface) { constructor(query:QueryResource, schema:QuerySchemaResourceInterface) {
super();
this.current = angular.copy(query.sortBy); this.current = angular.copy(query.sortBy);
this.available = angular.copy(schema.sortBy.allowedValues as QuerySortByResource[]); this.available = angular.copy(schema.sortBy.allowedValues as QuerySortByResource[]);
} }
public comparerFunction():(current:QuerySortByResource[]) => any {
return (current:QuerySortByResource[]) => current.map((el:HalResource) => el.href);
}
public addCurrent(sortBy:QuerySortByResource) { public addCurrent(sortBy:QuerySortByResource) {
this.current.unshift(sortBy); this.current.unshift(sortBy);

@ -26,10 +26,12 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
// ++ // ++
export class WorkPackageTableSum { import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableSum extends WorkPackageTableBaseState<boolean> {
public current:boolean; public current:boolean;
constructor(isSum:boolean) { constructor(isSum:boolean) {
super();
this.current = isSum; this.current = isSum;
} }

@ -26,10 +26,12 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
// ++ // ++
export class WorkPackageTableTimelineVisible { import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableTimelineVisible extends WorkPackageTableBaseState<boolean> {
public current:boolean; public current:boolean;
constructor(isVisible:boolean) { constructor(isVisible:boolean) {
super();
this.current = isVisible; this.current = isVisible;
} }

Loading…
Cancel
Save