mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
66391: expandedNodes (&loadingNode) is now retrieved/sent to the store
at component initialisation/destruction so the state of the tree persists & documentation
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { ActionReducerMap, createSelector, MemoizedSelector } from '@ngrx/store';
|
||||
import * as fromRouter from '@ngrx/router-store';
|
||||
import { CommunityListReducer, CommunityListState } from './community-list-page/community-list.reducer';
|
||||
import { hostWindowReducer, HostWindowState } from './shared/host-window.reducer';
|
||||
import { formReducer, FormState } from './shared/form/form.reducer';
|
||||
import {
|
||||
@@ -43,6 +44,7 @@ export interface AppState {
|
||||
cssVariables: CSSVariablesState;
|
||||
menus: MenusState;
|
||||
objectSelection: ObjectSelectionListState;
|
||||
communityList: CommunityListState;
|
||||
}
|
||||
|
||||
export const appReducers: ActionReducerMap<AppState> = {
|
||||
@@ -58,7 +60,8 @@ export const appReducers: ActionReducerMap<AppState> = {
|
||||
truncatable: truncatableReducer,
|
||||
cssVariables: cssVariablesReducer,
|
||||
menus: menusReducer,
|
||||
objectSelection: objectSelectionReducer
|
||||
objectSelection: objectSelectionReducer,
|
||||
communityList: CommunityListReducer,
|
||||
};
|
||||
|
||||
export const routerStateSelector = (state: AppState) => state.router;
|
||||
|
@@ -3,6 +3,12 @@ import { CollectionViewer, DataSource } from '@angular/cdk/typings/collections';
|
||||
import { BehaviorSubject, Observable, } from 'rxjs';
|
||||
import { finalize, take, } from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
* DataSource object needed by a CDK Tree to render its nodes.
|
||||
* The list of FlatNodes that this DataSource object represents gets created in the CommunityListService at
|
||||
* the beginning (initial page-limited top communities) and re-calculated any time the tree state changes
|
||||
* (a node gets expanded or page-limited result become larger by triggering a show more node)
|
||||
*/
|
||||
export class CommunityListDatasource implements DataSource<FlatNode> {
|
||||
|
||||
private communityList$ = new BehaviorSubject<FlatNode[]>([]);
|
||||
@@ -12,7 +18,6 @@ export class CommunityListDatasource implements DataSource<FlatNode> {
|
||||
}
|
||||
|
||||
connect(collectionViewer: CollectionViewer): Observable<FlatNode[]> {
|
||||
this.loadCommunities(null);
|
||||
return this.communityList$.asObservable();
|
||||
}
|
||||
|
||||
|
@@ -6,6 +6,9 @@ import { CommunityListPageRoutingModule } from './community-list-page.routing.mo
|
||||
import { CommunityListComponent } from './community-list/community-list.component';
|
||||
import { CdkTreeModule } from '@angular/cdk/tree';
|
||||
|
||||
/**
|
||||
* The page which houses a title and the community list, as described in community-list.component
|
||||
*/
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@@ -5,6 +5,9 @@ import { CdkTreeModule } from '@angular/cdk/tree';
|
||||
import { CommunityListPageComponent } from './community-list-page.component';
|
||||
import { CommunityListService } from './community-list-service';
|
||||
|
||||
/**
|
||||
* RouterModule to help navigate to the page with the community list tree
|
||||
*/
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { createSelector, Store } from '@ngrx/store';
|
||||
import { combineLatest as observableCombineLatest } from 'rxjs/internal/observable/combineLatest';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { AppState } from '../app.reducer';
|
||||
import { CommunityDataService } from '../core/data/community-data.service';
|
||||
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
|
||||
import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model';
|
||||
@@ -13,7 +15,13 @@ import {PaginatedList} from '../core/data/paginated-list';
|
||||
import { getCommunityPageRoute } from '../+community-page/community-page-routing.module';
|
||||
import { getCollectionPageRoute } from '../+collection-page/collection-page-routing.module';
|
||||
import { CollectionDataService } from '../core/data/collection-data.service';
|
||||
import { CommunityListSaveAction } from './community-list.actions';
|
||||
import { CommunityListState } from './community-list.reducer';
|
||||
|
||||
/**
|
||||
* Each node in the tree is represented by a flatNode which contains info about the node itself and its position and
|
||||
* state in the tree. There are nodes representing communities, collections and show more links.
|
||||
*/
|
||||
export interface FlatNode {
|
||||
isExpandable$: Observable<boolean>;
|
||||
name: string;
|
||||
@@ -28,15 +36,28 @@ export interface FlatNode {
|
||||
currentCollectionPage?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The show more links in the community tree are also represented by a flatNode so we know where in
|
||||
* the tree it should be rendered an who its parent is (needed for the action resulting in clicking this link)
|
||||
*/
|
||||
export class ShowMoreFlatNode {
|
||||
}
|
||||
|
||||
// Helper method to combine an flatten an array of observables of flatNode arrays
|
||||
export const combineAndFlatten = (obsList: Array<Observable<FlatNode[]>>): Observable<FlatNode[]> =>
|
||||
observableCombineLatest(...obsList).pipe(
|
||||
map((matrix: FlatNode[][]) =>
|
||||
matrix.reduce((combinedList, currentList: FlatNode[]) => [...combinedList, ...currentList]))
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a flatNode from a community or collection
|
||||
* @param c The community or collection this flatNode represents
|
||||
* @param isExpandable Whether or not this node is expandable (true if it has children)
|
||||
* @param level Level indicating how deep in the tree this node should be rendered
|
||||
* @param isExpanded Whether or not this node already is expanded
|
||||
* @param parent Parent of this node (flatNode representing its parent community)
|
||||
*/
|
||||
export const toFlatNode = (
|
||||
c: Community | Collection,
|
||||
isExpandable: Observable<boolean>,
|
||||
@@ -55,6 +76,9 @@ export const toFlatNode = (
|
||||
route: c instanceof Community ? getCommunityPageRoute(c.id) : getCollectionPageRoute(c.id),
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a show More flatnode where only the level and parent are of importance
|
||||
*/
|
||||
export const showMoreFlatNode = (
|
||||
id: string,
|
||||
level: number,
|
||||
@@ -70,6 +94,15 @@ export const showMoreFlatNode = (
|
||||
isShowMoreNode: true,
|
||||
});
|
||||
|
||||
// Selectors the get the communityList data out of the store
|
||||
const communityListStateSelector = (state: AppState) => state.communityList;
|
||||
const expandedNodesSelector = createSelector(communityListStateSelector, (communityList: CommunityListState) => communityList.expandedNodes);
|
||||
const loadingNodeSelector = createSelector(communityListStateSelector, (communityList: CommunityListState) => communityList.loadingNode);
|
||||
|
||||
/**
|
||||
* Service class for the community list, responsible for the creating of the flat list used by communityList dataSource
|
||||
* and connection to the store to retrieve and save the state of the community list
|
||||
*/
|
||||
// tslint:disable-next-line:max-classes-per-file
|
||||
@Injectable()
|
||||
export class CommunityListService {
|
||||
@@ -83,7 +116,8 @@ export class CommunityListService {
|
||||
maxSubCommunitiesPerPage: number;
|
||||
maxCollectionsPerPage: number;
|
||||
|
||||
constructor(private communityDataService: CommunityDataService, private collectionDataService: CollectionDataService) {
|
||||
constructor(private communityDataService: CommunityDataService, private collectionDataService: CollectionDataService,
|
||||
private store: Store<any>) {
|
||||
this.topCommunitiesConfig = new PaginationComponentOptions();
|
||||
this.topCommunitiesConfig.id = 'top-level-pagination';
|
||||
this.topCommunitiesConfig.pageSize = 10;
|
||||
@@ -95,6 +129,18 @@ export class CommunityListService {
|
||||
this.maxCollectionsPerPage = 3;
|
||||
}
|
||||
|
||||
saveCommunityListStateToStore(expandedNodes: FlatNode[], loadingNode: FlatNode): void {
|
||||
this.store.dispatch(new CommunityListSaveAction(expandedNodes, loadingNode));
|
||||
}
|
||||
|
||||
getExpandedNodesFromStore(): Observable<FlatNode[]> {
|
||||
return this.store.select(expandedNodesSelector);
|
||||
}
|
||||
|
||||
getLoadingNodeFromStore(): Observable<FlatNode> {
|
||||
return this.store.select(loadingNodeSelector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the payload so it contains the next page of top level communities
|
||||
*/
|
||||
@@ -114,8 +160,9 @@ export class CommunityListService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all top communities, limited by page, and transforms this in a list of flatnodes.
|
||||
* @param expandedNodes List of expanded nodes; if a node is not expanded its subcommunities and collections need not be added to the list
|
||||
* Gets all top communities, limited by page, and transforms this in a list of flatNodes.
|
||||
* @param expandedNodes List of expanded nodes; if a node is not expanded its subCommunities and collections need
|
||||
* not be added to the list
|
||||
*/
|
||||
loadCommunities(expandedNodes: FlatNode[]): Observable<FlatNode[]> {
|
||||
const res = this.payloads$.map((payload) => {
|
||||
|
35
src/app/community-list-page/community-list.actions.ts
Normal file
35
src/app/community-list-page/community-list.actions.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { type } from '../shared/ngrx/type';
|
||||
import { FlatNode } from './community-list-service';
|
||||
|
||||
/**
|
||||
* All the action types of the community-list
|
||||
*/
|
||||
|
||||
export const CommunityListActionTypes = {
|
||||
SAVE: type('dspace/community-list-page/SAVE')
|
||||
};
|
||||
|
||||
/**
|
||||
* Community list SAVE action
|
||||
*/
|
||||
export class CommunityListSaveAction implements Action {
|
||||
|
||||
type = CommunityListActionTypes.SAVE;
|
||||
|
||||
payload: {
|
||||
expandedNodes: FlatNode[];
|
||||
loadingNode: FlatNode;
|
||||
};
|
||||
|
||||
constructor(expandedNodes: FlatNode[], loadingNode: FlatNode) {
|
||||
this.payload = { expandedNodes, loadingNode }
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Export a type alias of all actions in this action group
|
||||
* so that reducers can easily compose action types
|
||||
*/
|
||||
|
||||
export type CommunityListActions = CommunityListSaveAction;
|
36
src/app/community-list-page/community-list.reducer.ts
Normal file
36
src/app/community-list-page/community-list.reducer.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { FlatNode } from './community-list-service';
|
||||
import { CommunityListActionTypes, CommunityListSaveAction } from './community-list.actions';
|
||||
|
||||
/**
|
||||
* States we wish to put in store concerning the community list
|
||||
*/
|
||||
export interface CommunityListState {
|
||||
expandedNodes: FlatNode[];
|
||||
loadingNode: FlatNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial starting state of the list of expandedNodes and the current loading node of the community list
|
||||
*/
|
||||
const initialState: CommunityListState = {
|
||||
expandedNodes: [],
|
||||
loadingNode: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Reducer to interact with store concerning objects for the community list
|
||||
* @constructor
|
||||
*/
|
||||
export function CommunityListReducer(state = initialState, action: CommunityListSaveAction) {
|
||||
switch (action.type) {
|
||||
case CommunityListActionTypes.SAVE: {
|
||||
return Object.assign({}, state, {
|
||||
expandedNodes: (action as CommunityListSaveAction).payload.expandedNodes,
|
||||
loadingNode: (action as CommunityListSaveAction).payload.loadingNode,
|
||||
})
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,14 +1,22 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { CommunityListService, FlatNode } from '../community-list-service';
|
||||
import { CommunityListDatasource } from '../community-list-datasource';
|
||||
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||
import { isEmpty } from '../../shared/empty.util';
|
||||
|
||||
/**
|
||||
* A tree-structured list of nodes representing the communities, their subCommunities and collections.
|
||||
* Initially only the page-restricted top communities are shown.
|
||||
* Each node can be expanded to show its children and all children are also page-limited.
|
||||
* More pages of a page-limited result can be shown by pressing a show more node/link.
|
||||
* Which nodes were expanded is kept in the store, so this persists across pages.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-community-list',
|
||||
templateUrl: './community-list.component.html',
|
||||
})
|
||||
export class CommunityListComponent implements OnInit {
|
||||
export class CommunityListComponent implements OnInit, OnDestroy {
|
||||
|
||||
private expandedNodes: FlatNode[] = [];
|
||||
public loadingNode: FlatNode;
|
||||
@@ -24,7 +32,17 @@ export class CommunityListComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.dataSource = new CommunityListDatasource(this.communityListService);
|
||||
this.dataSource.loadCommunities(null);
|
||||
this.communityListService.getLoadingNodeFromStore().pipe(take(1)).subscribe((result) => {
|
||||
this.loadingNode = result;
|
||||
});
|
||||
this.communityListService.getExpandedNodesFromStore().pipe(take(1)).subscribe((result) => {
|
||||
this.expandedNodes = [...result];
|
||||
this.dataSource.loadCommunities(this.expandedNodes);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.communityListService.saveCommunityListStateToStore(this.expandedNodes, this.loadingNode);
|
||||
}
|
||||
|
||||
// whether or not this node has children (subcommunities or collections)
|
||||
|
Reference in New Issue
Block a user