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 {WorkPackageTableBaseService} from "../../wp-fast-table/state/wp-table-base.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,
$rootScope:ng.IRootScopeService,
@ -109,77 +105,50 @@ function WorkPackagesListController($scope:any,
}
});
setupChangeObserver(wpTableSortBy, { attrName: 'sortBy', triggerUpdate: true, halMap: true });
setupChangeObserver(wpTableSum, { attrName: 'sums', triggerUpdate: true });
setupChangeObserver(wpTableTimeline, { attrName: 'timelineVisible', triggerUpdate: false });
setupChangeObserver(wpTableHierarchies, { attrName: 'showHierarchies', triggerUpdate: false });
setupChangeObserver(wpTableColumns, { attrName: 'columns', triggerUpdate: false, halMap: true });
// Filter objects do not have hrefs to compare them by, but we can use a copy of their source
// (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
}
);
setupChangeObserver(wpTableFilters, 'filters');
setupChangeObserver(wpTableGroupBy, 'groupBy');
setupChangeObserver(wpTableSortBy, 'sortBy');
setupChangeObserver(wpTableSum, 'sums');
setupChangeObserver(wpTableTimeline, 'timelineVisible', false);
setupChangeObserver(wpTableHierarchies, 'showHierarchies', false);
setupChangeObserver(wpTableColumns, 'columns', false);
}
function setupChangeObserver(service:WorkPackageTableBaseService, options:ObserverSetupOptions) {
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;
}
function setupChangeObserver(service:WorkPackageTableBaseService, name:string, triggerUpdate:boolean = true) {
service
.observeUntil(scopeDestroyed$($scope))
.distinctUntilChanged(
(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:any) => {
updateAndExecuteIfAltered(stateValue.current, options.attrName, options.triggerUpdate);
.subscribe((stateValue:WorkPackageTableBaseState<any>) => {
// Avoid updating while not all states are initialized
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() {
wpListChecksumService.clear();
loadingIndicator.table.promise = wpListService.fromQueryParams($state.params, $scope.projectIdentifier);

@ -28,7 +28,7 @@
import {InputState} from "reactivestates";
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 {Observable} from 'rxjs';
@ -46,10 +46,12 @@ export abstract class WorkPackageTableBaseService {
constructor(protected states: States) {
}
protected get state(): InputState<WorkPackageTableBaseInterface> {
protected get state(): InputState<any> {
return this.states.table[this.stateName];
};
public observeOnScope(scope:ng.IScope) {
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 {Observable} from 'rxjs/Observable';
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 {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 {States} from '../../states.service';
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 {
protected stateName = 'filters' as TableStateStates;

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

@ -26,6 +26,22 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
export interface WorkPackageTableBaseInterface {
current:any
export class WorkPackageTableBaseState<T> {
/**
* 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 {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service'
import {Observable} from 'rxjs/Observable';
import {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableColumns {
export class WorkPackageTableColumns extends WorkPackageTableBaseState<QueryColumn[]> {
// Available columns
public available:QueryColumn[]|undefined;
@ -40,6 +41,7 @@ export class WorkPackageTableColumns {
public current:QueryColumn[];
constructor(query:QueryResource, schema?:QuerySchemaResourceInterface) {
super();
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 {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 {WorkPackageTableBaseState} from "./wp-table-base";
export class WorkPackageTableFilters {
export class WorkPackageTableFilters extends WorkPackageTableBaseState<QueryFilterInstanceResource[]> {
public availableSchemas:QueryFilterInstanceSchemaResource[] = [];
public current:QueryFilterInstanceResource[] = [];
public comparerFunction():(current:QueryFilterInstanceResource[]) => any {
return (current:QueryFilterInstanceResource[]) => current.map((el:HalResource) => el.$plain());
}
constructor(filters:QueryFilterInstanceResource[], schema:QuerySchemaResourceInterface) {
super();
this.current = filters;
this.availableSchemas = schema
.filtersSchemas

@ -32,12 +32,18 @@ import {
QueryColumn
} from '../api/api-v3/hal-resources/query-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 current:QueryGroupByResource | undefined;
public comparerFunction():(current:QueryGroupByResource|undefined) => any {
return (current:QueryGroupByResource) => current && current.href;
}
constructor(query:QueryResource, schema?:QuerySchemaResourceInterface) {
super();
this.current = angular.copy(query.groupBy);
if (schema) {

@ -26,11 +26,13 @@
// 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 collapsed:{[workPackageId:string]:boolean};
constructor(isVisible:boolean) {
super();
this.current = isVisible;
this.collapsed = {};
}

@ -27,12 +27,14 @@
// ++
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,
public perPage:number,
public total:number,
public count:number) {
super();
}
}

@ -36,16 +36,23 @@ import {
QueryColumn
} from '../api/api-v3/hal-resources/query-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 current:QuerySortByResource[] = [];
constructor(query:QueryResource, schema:QuerySchemaResourceInterface) {
super();
this.current = angular.copy(query.sortBy);
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) {
this.current.unshift(sortBy);

@ -26,10 +26,12 @@
// 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;
constructor(isSum:boolean) {
super();
this.current = isSum;
}

@ -26,10 +26,12 @@
// 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;
constructor(isVisible:boolean) {
super();
this.current = isVisible;
}

Loading…
Cancel
Save