mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
fix issue where the menu wouldn't update if an option was added after the initial render
This commit is contained in:

committed by
Marie Verdonck

parent
d2523ade9b
commit
2f946ba2a3
@@ -5,7 +5,7 @@
|
|||||||
<div class="sidebar-collapsible">
|
<div class="sidebar-collapsible">
|
||||||
<span class="section-header-text">
|
<span class="section-header-text">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(section.id); injector: itemInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<ng-container *ngFor="let section of (sections | async)">
|
<ng-container *ngFor="let section of (sections | async)">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="sectionComponents.get(section.id); injector: sectionInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,4 +49,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import { Component, Injector, OnInit } from '@angular/core';
|
import { Component, Injector, OnInit } from '@angular/core';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { combineLatest as combineLatestObservable } from 'rxjs';
|
import { combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { of } from 'rxjs/internal/observable/of';
|
import { of } from 'rxjs/internal/observable/of';
|
||||||
import { first, map, take } from 'rxjs/operators';
|
import { first, map, take, tap, filter } from 'rxjs/operators';
|
||||||
import { AuthService } from '../../core/auth/auth.service';
|
import { AuthService } from '../../core/auth/auth.service';
|
||||||
import { ProcessDataService } from '../../core/data/processes/process-data.service';
|
import { ProcessDataService } from '../../core/data/processes/process-data.service';
|
||||||
import { ScriptDataService } from '../../core/data/processes/script-data.service';
|
import { ScriptDataService } from '../../core/data/processes/script-data.service';
|
||||||
@@ -99,7 +99,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
this.sidebarOpen = !collapsed;
|
this.sidebarOpen = !collapsed;
|
||||||
this.sidebarClosed = collapsed;
|
this.sidebarClosed = collapsed;
|
||||||
});
|
});
|
||||||
this.sidebarExpanded = combineLatestObservable(this.menuCollapsed, this.menuPreviewCollapsed)
|
this.sidebarExpanded = observableCombineLatest(this.menuCollapsed, this.menuPreviewCollapsed)
|
||||||
.pipe(
|
.pipe(
|
||||||
map(([collapsed, previewCollapsed]) => (!collapsed || !previewCollapsed))
|
map(([collapsed, previewCollapsed]) => (!collapsed || !previewCollapsed))
|
||||||
);
|
);
|
||||||
@@ -323,78 +323,81 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
* the export scripts exist and the current user is allowed to execute them
|
* the export scripts exist and the current user is allowed to execute them
|
||||||
*/
|
*/
|
||||||
createExportMenuSections() {
|
createExportMenuSections() {
|
||||||
const isAuthorized$: Observable<boolean> = this.authorizationService.isAuthorized(FeatureID.AdministratorOf);
|
const menuList = [
|
||||||
isAuthorized$.subscribe((authorized: boolean) => {
|
/* Export */
|
||||||
if (authorized) {
|
{
|
||||||
const metadataExportScriptExists$ = this.scriptDataService.scripWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME);
|
id: 'export',
|
||||||
metadataExportScriptExists$.subscribe((metadataExportScriptExists: boolean) => {
|
active: false,
|
||||||
const menuList = [
|
visible: true,
|
||||||
/* Export */
|
model: {
|
||||||
{
|
type: MenuItemType.TEXT,
|
||||||
id: 'export',
|
text: 'menu.section.export'
|
||||||
active: false,
|
} as TextMenuItemModel,
|
||||||
visible: true,
|
icon: 'sign-out-alt',
|
||||||
model: {
|
index: 3,
|
||||||
type: MenuItemType.TEXT,
|
shouldPersistOnRouteChange: true
|
||||||
text: 'menu.section.export'
|
},
|
||||||
} as TextMenuItemModel,
|
{
|
||||||
icon: 'sign-out-alt',
|
id: 'export_community',
|
||||||
index: 3
|
parentID: 'export',
|
||||||
},
|
active: false,
|
||||||
{
|
visible: true,
|
||||||
id: 'export_community',
|
model: {
|
||||||
parentID: 'export',
|
type: MenuItemType.LINK,
|
||||||
active: false,
|
text: 'menu.section.export_community',
|
||||||
visible: true,
|
link: ''
|
||||||
model: {
|
} as LinkMenuItemModel,
|
||||||
type: MenuItemType.LINK,
|
shouldPersistOnRouteChange: true
|
||||||
text: 'menu.section.export_community',
|
},
|
||||||
link: ''
|
{
|
||||||
} as LinkMenuItemModel,
|
id: 'export_collection',
|
||||||
},
|
parentID: 'export',
|
||||||
{
|
active: false,
|
||||||
id: 'export_collection',
|
visible: true,
|
||||||
parentID: 'export',
|
model: {
|
||||||
active: false,
|
type: MenuItemType.LINK,
|
||||||
visible: true,
|
text: 'menu.section.export_collection',
|
||||||
model: {
|
link: ''
|
||||||
type: MenuItemType.LINK,
|
} as LinkMenuItemModel,
|
||||||
text: 'menu.section.export_collection',
|
shouldPersistOnRouteChange: true
|
||||||
link: ''
|
},
|
||||||
} as LinkMenuItemModel,
|
{
|
||||||
},
|
id: 'export_item',
|
||||||
{
|
parentID: 'export',
|
||||||
id: 'export_item',
|
active: false,
|
||||||
parentID: 'export',
|
visible: true,
|
||||||
active: false,
|
model: {
|
||||||
visible: true,
|
type: MenuItemType.LINK,
|
||||||
model: {
|
text: 'menu.section.export_item',
|
||||||
type: MenuItemType.LINK,
|
link: ''
|
||||||
text: 'menu.section.export_item',
|
} as LinkMenuItemModel,
|
||||||
link: ''
|
shouldPersistOnRouteChange: true
|
||||||
} as LinkMenuItemModel,
|
},
|
||||||
},
|
];
|
||||||
{
|
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, menuSection));
|
||||||
id: 'export_metadata',
|
|
||||||
parentID: 'export',
|
|
||||||
active: true,
|
|
||||||
visible: (authorized && metadataExportScriptExists),
|
|
||||||
model: {
|
|
||||||
type: MenuItemType.ONCLICK,
|
|
||||||
text: 'menu.section.export_metadata',
|
|
||||||
function: () => {
|
|
||||||
this.modalService.open(ExportMetadataSelectorComponent);
|
|
||||||
}
|
|
||||||
} as OnClickMenuItemModel,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
menuList.forEach((menuSection) => this.menuService.addSection(this.menuID, Object.assign(menuSection, {
|
|
||||||
shouldPersistOnRouteChange: true
|
|
||||||
})));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
observableCombineLatest(
|
||||||
|
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||||
|
this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)
|
||||||
|
).pipe(
|
||||||
|
filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists),
|
||||||
|
take(1)
|
||||||
|
).subscribe(() => {
|
||||||
|
this.menuService.addSection(this.menuID, {
|
||||||
|
id: 'export_metadata',
|
||||||
|
parentID: 'export',
|
||||||
|
active: true,
|
||||||
|
visible: true,
|
||||||
|
model: {
|
||||||
|
type: MenuItemType.ONCLICK,
|
||||||
|
text: 'menu.section.export_metadata',
|
||||||
|
function: () => {
|
||||||
|
this.modalService.open(ExportMetadataSelectorComponent);
|
||||||
|
}
|
||||||
|
} as OnClickMenuItemModel,
|
||||||
|
shouldPersistOnRouteChange: true
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -12,16 +12,16 @@
|
|||||||
(click)="toggleSection($event)">
|
(click)="toggleSection($event)">
|
||||||
<span class="section-header-text">
|
<span class="section-header-text">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(section.id); injector: itemInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</span>
|
</span>
|
||||||
<i class="fas fa-chevron-right fa-pull-right"
|
<i class="fas fa-chevron-right fa-pull-right"
|
||||||
[@rotate]="(expanded | async) ? 'expanded' : 'collapsed'" [title]="('menu.section.toggle.' + section.id) | translate"></i>
|
[@rotate]="(expanded | async) ? 'expanded' : 'collapsed'" [title]="('menu.section.toggle.' + section.id) | translate"></i>
|
||||||
</a>
|
</a>
|
||||||
<ul class="sidebar-sub-level-items list-unstyled" @slide *ngIf="(expanded | async)">
|
<ul class="sidebar-sub-level-items list-unstyled" @slide *ngIf="(expanded | async)">
|
||||||
<li *ngFor="let subSection of (subSections | async)">
|
<li *ngFor="let subSection of (subSections$ | async)">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(subSection.id); injector: itemInjectors.get(subSection.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;"></ng-container>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { getSucceededRemoteData } from '../../shared/operators';
|
import {
|
||||||
|
getSucceededRemoteData,
|
||||||
|
getFirstSucceededRemoteDataPayload
|
||||||
|
} from '../../shared/operators';
|
||||||
import { DataService } from '../data.service';
|
import { DataService } from '../data.service';
|
||||||
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../../cache/builders/remote-data-build.service';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
@@ -11,7 +14,7 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
import { DefaultChangeAnalyzer } from '../default-change-analyzer.service';
|
import { DefaultChangeAnalyzer } from '../default-change-analyzer.service';
|
||||||
import { Script } from '../../../process-page/scripts/script.model';
|
import { Script } from '../../../process-page/scripts/script.model';
|
||||||
import { ProcessParameter } from '../../../process-page/processes/process-parameter.model';
|
import { ProcessParameter } from '../../../process-page/processes/process-parameter.model';
|
||||||
import { find, map, switchMap } from 'rxjs/operators';
|
import { find, map, switchMap, filter } from 'rxjs/operators';
|
||||||
import { URLCombiner } from '../../url-combiner/url-combiner';
|
import { URLCombiner } from '../../url-combiner/url-combiner';
|
||||||
import { RemoteData } from '../remote-data';
|
import { RemoteData } from '../remote-data';
|
||||||
import { MultipartPostRequest, RestRequest } from '../request.models';
|
import { MultipartPostRequest, RestRequest } from '../request.models';
|
||||||
@@ -20,6 +23,7 @@ import { Observable } from 'rxjs';
|
|||||||
import { RequestEntry } from '../request.reducer';
|
import { RequestEntry } from '../request.reducer';
|
||||||
import { dataService } from '../../cache/builders/build-decorators';
|
import { dataService } from '../../cache/builders/build-decorators';
|
||||||
import { SCRIPT } from '../../../process-page/scripts/script.resource-type';
|
import { SCRIPT } from '../../../process-page/scripts/script.resource-type';
|
||||||
|
import { hasValue } from '../../../shared/empty.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@dataService(SCRIPT)
|
@dataService(SCRIPT)
|
||||||
@@ -65,11 +69,11 @@ export class ScriptDataService extends DataService<Script> {
|
|||||||
* Check whether a script with given name exist; user needs to be allowed to execute script for this to to not throw a 401 Unauthorized
|
* Check whether a script with given name exist; user needs to be allowed to execute script for this to to not throw a 401 Unauthorized
|
||||||
* @param scriptName script we want to check exists (and we can execute)
|
* @param scriptName script we want to check exists (and we can execute)
|
||||||
*/
|
*/
|
||||||
public scripWithNameExistsAndCanExecute(scriptName: string): Observable<boolean> {
|
public scriptWithNameExistsAndCanExecute(scriptName: string): Observable<boolean> {
|
||||||
return this.findById(scriptName).pipe(
|
return this.findById(scriptName).pipe(
|
||||||
getSucceededRemoteData(),
|
find((rd: RemoteData<Script>) => hasValue(rd.payload) || hasValue(rd.error)),
|
||||||
map((scriptRD: RemoteData<Script>) => {
|
map((rd: RemoteData<Script>) => {
|
||||||
return (scriptRD.hasSucceeded);
|
return hasValue(rd.payload);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,13 +5,13 @@
|
|||||||
id="browseDropdown" (click)="toggleSection($event)"
|
id="browseDropdown" (click)="toggleSection($event)"
|
||||||
data-toggle="dropdown">
|
data-toggle="dropdown">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(section.id); injector: itemInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</a>
|
</a>
|
||||||
<ul @slide *ngIf="(active | async)" (click)="deactivateSection($event)"
|
<ul @slide *ngIf="(active | async)" (click)="deactivateSection($event)"
|
||||||
class="m-0 shadow-none border-top-0 dropdown-menu show">
|
class="m-0 shadow-none border-top-0 dropdown-menu show">
|
||||||
<ng-container *ngFor="let subSection of (subSections | async)">
|
<ng-container *ngFor="let subSection of (subSections$ | async)">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(subSection.id); injector: itemInjectors.get(subSection.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(subSection.id).component; injector: (sectionMap$ | async).get(subSection.id).injector;"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="itemComponents.get(section.id); injector: itemInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<ul class="navbar-nav mr-auto shadow-none">
|
<ul class="navbar-nav mr-auto shadow-none">
|
||||||
<ng-container *ngFor="let section of (sections | async)">
|
<ng-container *ngFor="let section of (sections | async)">
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngComponentOutlet="sectionComponents.get(section.id); injector: sectionInjectors.get(section.id);"></ng-container>
|
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
import { Component, Injector } from '@angular/core';
|
import { Component, Injector, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { MenuService } from '../menu.service';
|
import { MenuService } from '../menu.service';
|
||||||
import { MenuSection } from '../menu.reducer';
|
import { MenuSection } from '../menu.reducer';
|
||||||
import { getComponentForMenuItemType } from '../menu-item.decorator';
|
import { getComponentForMenuItemType } from '../menu-item.decorator';
|
||||||
import { MenuID, MenuItemType } from '../initial-menus-state';
|
import { MenuID, MenuItemType } from '../initial-menus-state';
|
||||||
import { hasNoValue } from '../../empty.util';
|
import { hasNoValue, hasValue } from '../../empty.util';
|
||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { MenuItemModel } from '../menu-item/models/menu-item.model';
|
import { MenuItemModel } from '../menu-item/models/menu-item.model';
|
||||||
import { distinctUntilChanged } from 'rxjs/operators';
|
import { distinctUntilChanged, switchMap } from 'rxjs/operators';
|
||||||
import { GenericConstructor } from '../../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../../core/shared/generic-constructor';
|
||||||
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic implementation of a menu section's component
|
* A basic implementation of a menu section's component
|
||||||
@@ -16,7 +18,7 @@ import { GenericConstructor } from '../../../core/shared/generic-constructor';
|
|||||||
selector: 'ds-menu-section',
|
selector: 'ds-menu-section',
|
||||||
template: ''
|
template: ''
|
||||||
})
|
})
|
||||||
export class MenuSectionComponent {
|
export class MenuSectionComponent implements OnInit, OnDestroy{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observable that emits whether or not this section is currently active
|
* Observable that emits whether or not this section is currently active
|
||||||
@@ -28,20 +30,25 @@ export class MenuSectionComponent {
|
|||||||
*/
|
*/
|
||||||
menuID: MenuID;
|
menuID: MenuID;
|
||||||
|
|
||||||
/**
|
|
||||||
* List of Injectors for each dynamically rendered menu item of this section
|
|
||||||
*/
|
|
||||||
itemInjectors: Map<string, Injector> = new Map<string, Injector>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of child Components for each dynamically rendered menu item of this section
|
|
||||||
*/
|
|
||||||
itemComponents: Map<string, GenericConstructor<MenuSectionComponent>> = new Map<string, GenericConstructor<MenuSectionComponent>>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of available subsections in this section
|
* List of available subsections in this section
|
||||||
*/
|
*/
|
||||||
subSections: Observable<MenuSection[]>;
|
subSections$: Observable<MenuSection[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of components and injectors for each dynamically rendered menu section
|
||||||
|
*/
|
||||||
|
sectionMap$: BehaviorSubject<Map<string, {
|
||||||
|
injector: Injector,
|
||||||
|
component: GenericConstructor<MenuSectionComponent>
|
||||||
|
}>> = new BehaviorSubject(new Map());
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
subs: Subscription[] = [];
|
||||||
|
|
||||||
constructor(public section: MenuSection, protected menuService: MenuService, protected injector: Injector) {
|
constructor(public section: MenuSection, protected menuService: MenuService, protected injector: Injector) {
|
||||||
}
|
}
|
||||||
@@ -85,15 +92,34 @@ export class MenuSectionComponent {
|
|||||||
* Method for initializing all injectors and component constructors for the menu items in this section
|
* Method for initializing all injectors and component constructors for the menu items in this section
|
||||||
*/
|
*/
|
||||||
private initializeInjectorData() {
|
private initializeInjectorData() {
|
||||||
this.itemInjectors.set(this.section.id, this.getItemModelInjector(this.section.model));
|
this.updateSectionMap(
|
||||||
this.itemComponents.set(this.section.id, this.getMenuItemComponent(this.section.model));
|
this.section.id,
|
||||||
this.subSections = this.menuService.getSubSectionsByParentID(this.menuID, this.section.id);
|
this.getItemModelInjector(this.section.model),
|
||||||
this.subSections.subscribe((sections: MenuSection[]) => {
|
this.getMenuItemComponent(this.section.model)
|
||||||
sections.forEach((section: MenuSection) => {
|
);
|
||||||
this.itemInjectors.set(section.id, this.getItemModelInjector(section.model));
|
this.subSections$ = this.menuService.getSubSectionsByParentID(this.menuID, this.section.id);
|
||||||
this.itemComponents.set(section.id, this.getMenuItemComponent(section.model));
|
this.subs.push(
|
||||||
|
this.subSections$.pipe(
|
||||||
|
// if you return an array from a switchMap it will emit each element as a separate event.
|
||||||
|
// So this switchMap is equivalent to a subscribe with a forEach inside
|
||||||
|
switchMap((sections: MenuSection[]) => sections)
|
||||||
|
).subscribe((section: MenuSection) => {
|
||||||
|
this.updateSectionMap(
|
||||||
|
section.id,
|
||||||
|
this.getItemModelInjector(section.model),
|
||||||
|
this.getMenuItemComponent(section.model)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the sectionMap
|
||||||
|
*/
|
||||||
|
private updateSectionMap(id, injector, component) {
|
||||||
|
const nextMap = this.sectionMap$.getValue();
|
||||||
|
nextMap.set(id, { injector, component });
|
||||||
|
this.sectionMap$.next(nextMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,4 +150,12 @@ export class MenuSectionComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from open subscriptions
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs
|
||||||
|
.filter((subscription) => hasValue(subscription))
|
||||||
|
.forEach((subscription) => subscription.unsubscribe());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,12 +3,14 @@ import { Observable } from 'rxjs/internal/Observable';
|
|||||||
import { MenuService } from './menu.service';
|
import { MenuService } from './menu.service';
|
||||||
import { MenuID } from './initial-menus-state';
|
import { MenuID } from './initial-menus-state';
|
||||||
import { MenuSection } from './menu.reducer';
|
import { MenuSection } from './menu.reducer';
|
||||||
import { distinctUntilChanged, first, map } from 'rxjs/operators';
|
import { distinctUntilChanged, first, map, tap, switchMap, take } from 'rxjs/operators';
|
||||||
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
import { GenericConstructor } from '../../core/shared/generic-constructor';
|
||||||
import { hasValue } from '../empty.util';
|
import { hasValue } from '../empty.util';
|
||||||
import { MenuSectionComponent } from './menu-section/menu-section.component';
|
import { MenuSectionComponent } from './menu-section/menu-section.component';
|
||||||
import { getComponentForMenu } from './menu-section.decorator';
|
import { getComponentForMenu } from './menu-section.decorator';
|
||||||
import { Subscription } from 'rxjs/internal/Subscription';
|
import { Subscription } from 'rxjs/internal/Subscription';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { compareArraysUsingIds } from '../../+item-page/simple/item-types/shared/item-relationships-utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic implementation of a MenuComponent
|
* A basic implementation of a MenuComponent
|
||||||
@@ -44,14 +46,12 @@ export class MenuComponent implements OnInit, OnDestroy {
|
|||||||
sections: Observable<MenuSection[]>;
|
sections: Observable<MenuSection[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of Injectors for each dynamically rendered menu section
|
* Map of components and injectors for each dynamically rendered menu section
|
||||||
*/
|
*/
|
||||||
sectionInjectors: Map<string, Injector> = new Map<string, Injector>();
|
sectionMap$: BehaviorSubject<Map<string, {
|
||||||
|
injector: Injector,
|
||||||
/**
|
component: GenericConstructor<MenuSectionComponent>
|
||||||
* List of child Components for each dynamically rendered menu section
|
}>> = new BehaviorSubject(new Map());
|
||||||
*/
|
|
||||||
sectionComponents: Map<string, GenericConstructor<MenuSectionComponent>> = new Map<string, GenericConstructor<MenuSectionComponent>>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent unnecessary rerendering
|
* Prevent unnecessary rerendering
|
||||||
@@ -79,13 +79,25 @@ export class MenuComponent implements OnInit, OnDestroy {
|
|||||||
this.menuCollapsed = this.menuService.isMenuCollapsed(this.menuID);
|
this.menuCollapsed = this.menuService.isMenuCollapsed(this.menuID);
|
||||||
this.menuPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID);
|
this.menuPreviewCollapsed = this.menuService.isMenuPreviewCollapsed(this.menuID);
|
||||||
this.menuVisible = this.menuService.isMenuVisible(this.menuID);
|
this.menuVisible = this.menuService.isMenuVisible(this.menuID);
|
||||||
this.sections = this.menuService.getMenuTopSections(this.menuID).pipe(distinctUntilChanged((x, y) => JSON.stringify(x) === JSON.stringify(y)));
|
this.sections = this.menuService.getMenuTopSections(this.menuID).pipe(distinctUntilChanged(compareArraysUsingIds()));
|
||||||
this.subs.push(this.sections.subscribe((sections: MenuSection[]) => {
|
this.subs.push(
|
||||||
sections.forEach((section: MenuSection) => {
|
this.sections.pipe(
|
||||||
this.sectionInjectors.set(section.id, this.getSectionDataInjector(section));
|
// if you return an array from a switchMap it will emit each element as a separate event.
|
||||||
this.getSectionComponent(section).pipe(first()).subscribe((constr) => this.sectionComponents.set(section.id, constr));
|
// So this switchMap is equivalent to a subscribe with a forEach inside
|
||||||
});
|
switchMap((sections: MenuSection[]) => sections),
|
||||||
}));
|
switchMap((section: MenuSection) => this.getSectionComponent(section).pipe(
|
||||||
|
map((component: GenericConstructor<MenuSectionComponent>) => ({ section, component }))
|
||||||
|
)),
|
||||||
|
distinctUntilChanged((x,y) => x.section.id === y.section.id)
|
||||||
|
).subscribe(({ section, component}) => {
|
||||||
|
const nextMap = this.sectionMap$.getValue();
|
||||||
|
nextMap.set(section.id, {
|
||||||
|
injector: this.getSectionDataInjector(section),
|
||||||
|
component
|
||||||
|
});
|
||||||
|
this.sectionMap$.next(nextMap);
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user