Refactor wp-set in common work-packges-view-base

Use the previous wp-set as a common component for all multi-wp views
(embedded tables, wp-list, wp-calendar,...) and make that component
the parent of embedded tables.
pull/7079/head
Oliver Günther 6 years ago
parent 0b4a983a01
commit a923b8fbef
No known key found for this signature in database
GPG Key ID: A3A8BDAD7C0C552C
  1. 22
      frontend/src/app/components/wp-table/embedded/wp-embedded-base.component.ts
  2. 24
      frontend/src/app/components/wp-table/embedded/wp-embedded-table.component.ts
  3. 6
      frontend/src/app/modules/calendar/wp-calendar-entry/wp-calendar-entry.component.html
  4. 22
      frontend/src/app/modules/calendar/wp-calendar-entry/wp-calendar-entry.component.ts
  5. 2
      frontend/src/app/modules/grids/widgets/wp-widget/wp-widget.component.ts
  6. 4
      frontend/src/app/modules/work_packages/routing/wp-full-view/wp-full-view.component.ts
  7. 85
      frontend/src/app/modules/work_packages/routing/wp-list/wp-list.component.ts
  8. 4
      frontend/src/app/modules/work_packages/routing/wp-split-view/wp-split-view.component.ts
  9. 2
      frontend/src/app/modules/work_packages/routing/wp-view-base/work-package-single-view.base.ts
  10. 162
      frontend/src/app/modules/work_packages/routing/wp-view-base/work-packages-view.base.ts

@ -11,8 +11,9 @@ import {QueryDmService} from 'core-app/modules/hal/dm-services/query-dm.service'
import {UrlParamsHelperService} from 'core-components/wp-query/url-params-helper';
import {I18nService} from "core-app/modules/common/i18n/i18n.service";
import {IsolatedQuerySpace} from "core-app/modules/work_packages/query-space/isolated-query-space";
import {WorkPackagesViewBase} from "core-app/modules/work_packages/routing/wp-view-base/work-packages-view.base";
export abstract class WorkPackageEmbeddedBaseComponent implements OnInit, AfterViewInit, OnDestroy {
export abstract class WorkPackageEmbeddedBaseComponent extends WorkPackagesViewBase implements AfterViewInit {
@Input('configuration') protected providedConfiguration:WorkPackageTableConfigurationObject;
@Input() public uniqueEmbeddedTableName:string = `embedded-table-${Date.now()}`;
@Input() public initialLoadingIndicator:boolean = true;
@ -32,10 +33,9 @@ export abstract class WorkPackageEmbeddedBaseComponent implements OnInit, AfterV
readonly wpStatesInitialization:WorkPackageStatesInitializationService = this.injector.get(WorkPackageStatesInitializationService);
readonly currentProject:CurrentProjectService = this.injector.get(CurrentProjectService);
protected constructor(protected injector:Injector) {
}
ngOnInit() {
super.ngOnInit();
this.configuration = new WorkPackageTableConfiguration(this.providedConfiguration);
// Set embedded status in configuration
this.configuration.isEmbedded = true;
@ -45,16 +45,10 @@ export abstract class WorkPackageEmbeddedBaseComponent implements OnInit, AfterV
ngAfterViewInit():void {
// Load initially
this.refresh(this.initialLoadingIndicator);
// Reload results on refresh requests
this.querySpace.refreshRequired
.values$()
.pipe(untilComponentDestroyed(this))
.subscribe(() => this.refresh(false));
}
ngOnDestroy():void {
// noting to do
super.ngOnInit();
}
ngOnChanges(changes:SimpleChanges) {
@ -86,8 +80,8 @@ export abstract class WorkPackageEmbeddedBaseComponent implements OnInit, AfterV
this.renderTable = this.configuration.tableVisible;
}
public refresh(visible:boolean = true):Promise<any> {
return this.loadQuery(visible);
public refresh(visible:boolean = true, firstPage:boolean = false):Promise<any> {
return this.loadQuery(visible, firstPage);
}
public get isInitialized() {
@ -102,7 +96,7 @@ export abstract class WorkPackageEmbeddedBaseComponent implements OnInit, AfterV
}
}
protected abstract loadQuery(visible:boolean):Promise<any>;
protected abstract loadQuery(visible:boolean, firstPage:boolean):Promise<any>;
protected get queryProjectScope() {
if (!this.configuration.projectContext) {

@ -1,4 +1,4 @@
import {AfterViewInit, Component, EventEmitter, Injector, Input, OnDestroy, OnInit, Output} from '@angular/core';
import {AfterViewInit, Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
import {WorkPackageTableTimelineService} from 'core-components/wp-fast-table/state/wp-table-timeline.service';
import {WorkPackageTablePaginationService} from 'core-components/wp-fast-table/state/wp-table-pagination.service';
import {withLatestFrom} from 'rxjs/operators';
@ -14,7 +14,6 @@ import {WorkPackageEmbeddedBaseComponent} from "core-components/wp-table/embedde
import {WorkPackageTableFilters} from "core-components/wp-fast-table/wp-table-filters";
import {QueryFormResource} from "core-app/modules/hal/resources/query-form-resource";
import {QueryFormDmService} from "core-app/modules/hal/dm-services/query-form-dm.service";
import {FormResource} from "core-app/modules/hal/resources/form-resource";
@Component({
selector: 'wp-embedded-table',
@ -39,10 +38,6 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo
// Cache the form promise
private formPromise:Promise<QueryFormResource>|undefined;
constructor(readonly injector:Injector) {
super(injector);
}
ngAfterViewInit():void {
super.ngAfterViewInit();
@ -50,16 +45,6 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo
if (this.tableActions) {
this.tableActionsService.setActions(...this.tableActions);
}
// Reload results on changes to pagination
this.querySpace.ready.fireOnStateChange(this.wpTablePagination.state,
'Query loaded').values$().pipe(
untilComponentDestroyed(this),
withLatestFrom(this.querySpace.query.values$())
).subscribe(([pagination, query]) => {
this.loadingIndicator = this.QueryDm.loadResults(query, this.wpTablePagination.paginationObject)
.then((results) => this.initializeStates(query, results));
});
}
public openConfigurationModal(onUpdated:() => void) {
@ -113,7 +98,7 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo
.catch(() => this.formPromise = undefined);
}
protected loadQuery(visible:boolean = true):Promise<QueryResource> {
protected loadQuery(visible:boolean = true, firstPage:boolean = false):Promise<QueryResource> {
if (this.loadedQuery) {
const query = this.loadedQuery;
this.loadedQuery = undefined;
@ -131,6 +116,11 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo
this.queryProps.pageSize = this.configuration.forcePerPageOption;
}
// Set first page
if (firstPage) {
this.queryProps.page = 1;
}
this.error = null;
const promise = this.QueryDm
.find(

@ -1,4 +1,5 @@
<div class="work-packages-list-view--container">
<div class="work-packages-list-view--container loading-indicator--location"
data-indicator-name="calendar-entry">
<div class="toolbar-container -editable">
<div class="toolbar">
<div class="title-container">
@ -7,8 +8,7 @@
</h2>
</div>
<ul class="toolbar-items hide-when-print"
*ngIf="tableInformationLoaded">
<ul class="toolbar-items hide-when-print">
<li class="toolbar-item hidden-for-mobile">
<wp-filter-button>
</wp-filter-button>

@ -26,16 +26,26 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
import {Component} from '@angular/core';
import {WorkPackagesSetComponent} from "core-app/modules/work_packages/routing/wp-set/wp-set.component";
import {Component, ViewChild} from '@angular/core';
import {WorkPackagesViewBase} from "core-app/modules/work_packages/routing/wp-view-base/work-packages-view.base";
import {WorkPackagesCalendarController} from "core-app/modules/calendar/wp-calendar/wp-calendar.component";
@Component({
templateUrl: './wp-calendar-entry.component.html'
})
export class WorkPackagesCalendarEntryComponent extends WorkPackagesSetComponent {
// overrides super
protected initialQueryLoading() {
// nothing
export class WorkPackagesCalendarEntryComponent extends WorkPackagesViewBase {
@ViewChild(WorkPackagesCalendarController) calendarElement:WorkPackagesCalendarController;
/** Project identifier of the list */
projectIdentifier = this.$state.params['projectPath'] || null;
protected set loadingIndicator(promise:Promise<unknown>) {
this.loadingIndicatorService.indicator('calendar-entry').promise = promise;
}
public refresh(visibly:boolean, firstPage:boolean):Promise<unknown> {
return this.loadingIndicator =
this.wpListService.loadCurrentQueryFromParams(this.projectIdentifier);
}
}

@ -15,7 +15,7 @@ export class WidgetWpListComponent extends AbstractWidgetComponent implements On
public configuration:Partial<WorkPackageTableConfiguration> = {
actionsColumnEnabled: false,
columnMenuEnabled: false,
hierarchyToggleEnabled: false,
hierarchyToggleEnabled: true,
contextMenuEnabled: false
};

@ -28,7 +28,6 @@
import {UserResource} from 'core-app/modules/hal/resources/user-resource';
import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource';
import {WorkPackageViewController} from '../wp-view-base/wp-view-base.controller';
import {WorkPackageTableFocusService} from 'core-components/wp-fast-table/state/wp-table-focus.service';
import {StateService} from '@uirouter/core';
import {TypeResource} from 'core-app/modules/hal/resources/type-resource';
@ -37,6 +36,7 @@ import {WorkPackageTableSelection} from 'core-components/wp-fast-table/state/wp-
import {States} from 'core-components/states.service';
import {KeepTabService} from 'core-components/wp-single-view-tabs/keep-tab/keep-tab.service';
import {FirstRouteService} from "core-app/modules/router/first-route-service";
import {WorkPackageSingleViewBase} from "core-app/modules/work_packages/routing/wp-view-base/work-package-single-view.base";
@Component({
templateUrl: './wp-full-view.html',
@ -44,7 +44,7 @@ import {FirstRouteService} from "core-app/modules/router/first-route-service";
// Required class to support inner scrolling on page
host: { 'class': 'work-packages-page--ui-view' }
})
export class WorkPackagesFullViewComponent extends WorkPackageViewController {
export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase {
// Watcher properties
public isWatched:boolean;

@ -30,13 +30,14 @@ import {Component, OnDestroy} from "@angular/core";
import {untilComponentDestroyed} from 'ng2-rx-componentdestroyed';
import {QueryResource} from 'core-app/modules/hal/resources/query-resource';
import {OpTitleService} from "core-components/html/op-title.service";
import {WorkPackagesSetComponent} from "core-app/modules/work_packages/routing/wp-set/wp-set.component";
import {WorkPackagesViewBase} from "core-app/modules/work_packages/routing/wp-view-base/work-packages-view.base";
import {take} from "rxjs/operators";
@Component({
selector: 'wp-list',
templateUrl: './wp.list.component.html'
})
export class WorkPackagesListComponent extends WorkPackagesSetComponent implements OnDestroy {
export class WorkPackagesListComponent extends WorkPackagesViewBase implements OnDestroy {
text = {
'jump_to_pagination': this.I18n.t('js.work_packages.jump_marks.pagination'),
'text_jump_to_pagination': this.I18n.t('js.work_packages.jump_marks.label_pagination'),
@ -52,13 +53,32 @@ export class WorkPackagesListComponent extends WorkPackagesSetComponent implemen
/** Whether we're saving the query */
querySaving:boolean;
/** Listener callbacks */
unRegisterTitleListener:Function;
removeTransitionSubscription:Function;
/** Determine when query is initially loaded */
tableInformationLoaded = false;
/** Project identifier of the list */
projectIdentifier = this.$state.params['projectPath'] || null;
private readonly titleService:OpTitleService = this.injector.get(OpTitleService);
ngOnInit() {
super.ngOnInit();
// Load query initially
this.wpTableRefresh.clear('Impending query loading.');
this.loadCurrentQuery();
// Load query on URL transitions
this.updateQueryOnParamsChanges();
// Mark tableInformationLoaded when initially loading done
this.setupInformationLoadedListener();
// Update title on entering this state
this.unRegisterTitleListener = this.$transitions.onSuccess({to: 'work-packages.list'}, () => {
if (this.selectedTitle) {
@ -78,6 +98,7 @@ export class WorkPackagesListComponent extends WorkPackagesSetComponent implemen
ngOnDestroy():void {
super.ngOnDestroy();
this.unRegisterTitleListener();
this.removeTransitionSubscription();
}
public setAnchorToNextElement() {
@ -119,10 +140,60 @@ export class WorkPackagesListComponent extends WorkPackagesSetComponent implemen
}
}
protected loadCurrentQuery() {
return super.loadCurrentQuery()
.then(() => {
return this.querySpace.rendered.valuesPromise();
});
public refresh(visibly:boolean = false, firstPage:boolean = false):Promise<unknown> {
let promise:Promise<unknown>;
if (firstPage) {
promise = this.wpListService.loadCurrentResultsListFirstPage();
} else {
promise = this.wpListService.reloadCurrentResultsList();
}
if (visibly) {
this.loadingIndicator = promise;
}
return promise;
}
protected updateQueryOnParamsChanges() {
// Listen for param changes
this.removeTransitionSubscription = this.$transitions.onSuccess({}, (transition):any => {
let options = transition.options();
// Avoid performing any changes when we're going to reload
if (options.reload || (options.custom && options.custom.notify === false)) {
return true;
}
const params = transition.params('to');
let newChecksum = this.wpListService.getCurrentQueryProps(params);
let newId = params.query_id && parseInt(params.query_id);
this.wpListChecksumService
.executeIfOutdated(newId,
newChecksum,
() => this.loadCurrentQuery());
});
}
protected setupInformationLoadedListener() {
this.querySpace.tableRendering.onQueryUpdated
.values$()
.pipe(
take(1)
)
.subscribe(() => this.tableInformationLoaded = true);
}
protected set loadingIndicator(promise:Promise<unknown>) {
this.loadingIndicatorService.table.promise = promise;
}
protected loadCurrentQuery():Promise<unknown> {
return this.loadingIndicator =
this.wpListService
.loadCurrentQueryFromParams(this.projectIdentifier)
.then(() => this.querySpace.rendered.valuesPromise());
}
}

@ -31,17 +31,17 @@ import {StateService} from '@uirouter/core';
import {WorkPackageTableFocusService} from 'core-components/wp-fast-table/state/wp-table-focus.service';
import {componentDestroyed} from 'ng2-rx-componentdestroyed';
import {takeUntil} from 'rxjs/operators';
import {WorkPackageViewController} from '../wp-view-base/wp-view-base.controller';
import {States} from "core-components/states.service";
import {FirstRouteService} from "core-app/modules/router/first-route-service";
import {KeepTabService} from "core-components/wp-single-view-tabs/keep-tab/keep-tab.service";
import {WorkPackageTableSelection} from "core-components/wp-fast-table/state/wp-table-selection.service";
import {WorkPackageSingleViewBase} from "core-app/modules/work_packages/routing/wp-view-base/work-package-single-view.base";
@Component({
templateUrl: './wp-split-view.html',
selector: 'wp-split-view-entry',
})
export class WorkPackageSplitViewComponent extends WorkPackageViewController {
export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase {
constructor(public injector:Injector,
public states:States,

@ -45,7 +45,7 @@ import {
IWorkPackageEditingServiceToken
} from "core-components/wp-edit-form/work-package-editing.service.interface";
export class WorkPackageViewController implements OnDestroy {
export class WorkPackageSingleViewBase implements OnDestroy {
public wpCacheService:WorkPackageCacheService = this.injector.get(WorkPackageCacheService);
public states:States = this.injector.get(States);

@ -53,65 +53,45 @@ import {WorkPackageQueryStateService} from "core-components/wp-fast-table/state/
import {debugLog} from "core-app/helpers/debug_output";
import {WorkPackageFiltersService} from "core-components/filters/wp-filters/wp-filters.service";
export class WorkPackagesSetComponent implements OnInit, OnDestroy {
projectIdentifier = this.$state.params['projectPath'] || null;
tableInformationLoaded = false;
private removeTransitionSubscription:Function;
constructor(readonly injector:Injector,
readonly states:States,
readonly querySpace:IsolatedQuerySpace,
readonly authorisationService:AuthorisationService,
readonly wpTableRefresh:WorkPackageTableRefreshService,
readonly wpTableColumns:WorkPackageTableColumnsService,
readonly wpTableHighlighting:WorkPackageTableHighlightingService,
readonly wpTableSortBy:WorkPackageTableSortByService,
readonly wpTableGroupBy:WorkPackageTableGroupByService,
readonly wpTableFilters:WorkPackageTableFiltersService,
readonly wpTableSum:WorkPackageTableSumService,
readonly wpTableTimeline:WorkPackageTableTimelineService,
readonly wpTableHierarchies:WorkPackageTableHierarchiesService,
readonly wpTablePagination:WorkPackageTablePaginationService,
readonly wpListService:WorkPackagesListService,
readonly wpFilters:WorkPackageFiltersService,
readonly wpListChecksumService:WorkPackagesListChecksumService,
readonly loadingIndicator:LoadingIndicatorService,
readonly $transitions:TransitionService,
readonly $state:StateService,
readonly I18n:I18nService,
readonly wpStaticQueries:WorkPackageStaticQueriesService) {
export abstract class WorkPackagesViewBase implements OnInit, OnDestroy {
readonly $state:StateService = this.injector.get(StateService);
readonly states:States = this.injector.get(States);
readonly querySpace:IsolatedQuerySpace = this.injector.get(IsolatedQuerySpace);
readonly authorisationService:AuthorisationService = this.injector.get(AuthorisationService);
readonly wpTableRefresh:WorkPackageTableRefreshService = this.injector.get(WorkPackageTableRefreshService);
readonly wpTableColumns:WorkPackageTableColumnsService = this.injector.get(WorkPackageTableColumnsService);
readonly wpTableHighlighting:WorkPackageTableHighlightingService = this.injector.get(WorkPackageTableHighlightingService);
readonly wpTableSortBy:WorkPackageTableSortByService = this.injector.get(WorkPackageTableSortByService);
readonly wpTableGroupBy:WorkPackageTableGroupByService = this.injector.get(WorkPackageTableGroupByService);
readonly wpTableFilters:WorkPackageTableFiltersService = this.injector.get(WorkPackageTableFiltersService);
readonly wpTableSum:WorkPackageTableSumService = this.injector.get(WorkPackageTableSumService);
readonly wpTableTimeline:WorkPackageTableTimelineService = this.injector.get(WorkPackageTableTimelineService);
readonly wpTableHierarchies:WorkPackageTableHierarchiesService = this.injector.get(WorkPackageTableHierarchiesService);
readonly wpTablePagination:WorkPackageTablePaginationService = this.injector.get(WorkPackageTablePaginationService);
readonly wpListService:WorkPackagesListService = this.injector.get(WorkPackagesListService);
readonly wpListChecksumService:WorkPackagesListChecksumService = this.injector.get(WorkPackagesListChecksumService);
readonly loadingIndicatorService:LoadingIndicatorService = this.injector.get(LoadingIndicatorService);
readonly $transitions:TransitionService = this.injector.get(TransitionService);
readonly I18n:I18nService = this.injector.get(I18nService);
readonly wpStaticQueries:WorkPackageStaticQueriesService = this.injector.get(WorkPackageStaticQueriesService);
constructor(protected injector:Injector) {
}
ngOnInit() {
// Listen to changes on the query state objects
this.setupQueryObservers();
this.initialQueryLoading();
// Listen for refresh changes
this.setupRefreshObserver();
this.updateQueryOnParamsChanges();
}
ngOnDestroy():void {
if (this.removeTransitionSubscription) {
this.removeTransitionSubscription();
}
this.wpTableRefresh.clear('Table controller scope destroyed.');
}
private setupQueryObservers() {
this.querySpace.tableRendering.onQueryUpdated.values$()
.pipe(
take(1)
)
.subscribe(() => this.tableInformationLoaded = true);
this.querySpace.ready.fireOnStateChange(this.wpTablePagination.state,
'Query loaded').values$().pipe(
untilComponentDestroyed(this),
@ -119,7 +99,7 @@ export class WorkPackagesSetComponent implements OnInit, OnDestroy {
).subscribe(([pagination, query]) => {
if (this.wpListChecksumService.isQueryOutdated(query, pagination)) {
this.wpListChecksumService.update(query, pagination);
this.updateResultsVisibly();
this.refresh(true, false);
}
});
@ -133,13 +113,23 @@ export class WorkPackagesSetComponent implements OnInit, OnDestroy {
this.setupChangeObserver(this.wpTableHighlighting);
}
setupChangeObserver(service:WorkPackageQueryStateService, firstPage:boolean = false) {
/**
* Listen to changes in the given service and reload the query / results if
* the service requests that.
*
* @param service Work package query state service to listento
* @param firstPage If the service requests a change, load the first page
*/
protected setupChangeObserver(service:WorkPackageQueryStateService, firstPage:boolean = false) {
const queryState = this.querySpace.query;
this.querySpace.ready.fireOnStateChange(service.state, 'Query loaded').values$().pipe(
untilComponentDestroyed(this),
filter(() => queryState.hasValue() && service.hasChanged(queryState.value!))
).subscribe(() => {
this.querySpace.ready
.fireOnStateChange(service.state, 'Query loaded')
.values$()
.pipe(
untilComponentDestroyed(this),
filter(() => queryState.hasValue() && service.hasChanged(queryState.value!))
).subscribe(() => {
const newQuery = queryState.value!;
const triggerUpdate = service.applyToQuery(newQuery);
this.querySpace.query.putValue(newQuery);
@ -158,68 +148,30 @@ export class WorkPackagesSetComponent implements OnInit, OnDestroy {
* Setup the listener for members of the table to request a refresh of the entire table
* through the refresh service.
*/
setupRefreshObserver() {
this.wpTableRefresh.state.values$('Refresh listener in wp-list.controller').pipe(
protected setupRefreshObserver() {
this.wpTableRefresh.state.values$('Refresh listener in wp-set.component').pipe(
untilComponentDestroyed(this),
auditTime(20)
).subscribe(([refreshVisibly, firstPage]) => {
if (refreshVisibly) {
debugLog('Refreshing work package results visibly.');
this.updateResultsVisibly(firstPage);
} else {
debugLog('Refreshing work package results in the background.');
this.updateResults();
}
debugLog('Refreshing work package results.');
this.refresh(refreshVisibly, firstPage);
});
}
updateResults():Promise<WorkPackageCollectionResource> {
return this.wpListService.reloadCurrentResultsList();
}
updateResultsVisibly(firstPage:boolean = false) {
if (firstPage) {
this.loadingIndicator.table.promise = this.updateToFirstResultsPage();
} else {
this.loadingIndicator.table.promise = this.updateResults();
}
}
updateToFirstResultsPage():Promise<WorkPackageCollectionResource> {
return this.wpListService.loadCurrentResultsListFirstPage();
}
private updateQueryOnParamsChanges() {
// Listen for param changes
this.removeTransitionSubscription = this.$transitions.onSuccess({}, (transition):any => {
let options = transition.options();
// Avoid performing any changes when we're going to reload
if (options.reload || (options.custom && options.custom.notify === false)) {
return true;
}
const params = transition.params('to');
let newChecksum = this.wpListService.getCurrentQueryProps(params);
let newId = params.query_id && parseInt(params.query_id);
this.wpListChecksumService
.executeIfOutdated(newId,
newChecksum,
() => this.loadCurrentQuery());
});
}
protected initialQueryLoading() {
this.wpTableRefresh.clear('Impending query loading.');
this.loadCurrentQuery();
}
protected loadCurrentQuery():Promise<any> {
let loadingPromise = this.wpListService.loadCurrentQueryFromParams(this.projectIdentifier);
/**
* Refresh the set of results,
* showing the loading indicator if visibly is set.
*
* @param visibly Set true when desiring the loading indicator
* @param firstPage Reload the page to first page.
*/
public abstract refresh(visibly:boolean, firstPage:boolean):Promise<unknown>;
this.loadingIndicator.table.promise = loadingPromise;
return loadingPromise;
}
/**
* Set the loading indicator for this set instance
* @param promise
*/
protected abstract set loadingIndicator(promise:Promise<unknown>);
}
Loading…
Cancel
Save