diff --git a/src/app/browse-by/abstract-browse-by-type.component.ts b/src/app/browse-by/abstract-browse-by-type.component.ts deleted file mode 100644 index 5bd39cfc50..0000000000 --- a/src/app/browse-by/abstract-browse-by-type.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, Input, OnDestroy } from '@angular/core'; -import { BrowseByDataType } from './browse-by-switcher/browse-by-data-type'; -import { Context } from '../core/shared/context.model'; -import { Subscription } from 'rxjs'; -import { hasValue } from '../shared/empty.util'; - -@Component({ - selector: 'ds-abstract-browse-by-type', - template: '', -}) -export abstract class AbstractBrowseByTypeComponent implements OnDestroy { - - /** - * The optional context - */ - @Input() context: Context; - - /** - * The {@link BrowseByDataType} of this Component - */ - @Input() browseByType: BrowseByDataType; - - /** - * List of subscriptions - */ - subs: Subscription[] = []; - - ngOnDestroy(): void { - this.subs.filter((sub: Subscription) => hasValue(sub)).forEach((sub: Subscription) => sub.unsubscribe()); - } - -} diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 32b60ea3ca..8555fd3426 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -1,5 +1,5 @@ -import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; -import { Component, Inject, OnInit, OnDestroy } from '@angular/core'; +import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; +import { Component, Inject, OnInit, OnDestroy, Input } from '@angular/core'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; @@ -23,8 +23,8 @@ import { Community } from '../../core/shared/community.model'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; +import { Context } from '../../core/shared/context.model'; export const BBM_PAGINATION_ID = 'bbm'; @@ -40,7 +40,17 @@ export const BBM_PAGINATION_ID = 'bbm'; * 'dc.contributor.*' */ @rendersBrowseBy(BrowseByDataType.Metadata) -export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent implements OnInit, OnDestroy { +export class BrowseByMetadataPageComponent implements OnInit, OnDestroy { + + /** + * The optional context + */ + @Input() context: Context; + + /** + * The {@link BrowseByDataType} of this Component + */ + @Input() browseByType: BrowseByDataType; /** * The list of browse-entries to display @@ -77,6 +87,11 @@ export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent */ currentSort$: Observable; + /** + * List of subscriptions + */ + subs: Subscription[] = []; + /** * The default browse id to resort to when none is provided */ @@ -129,7 +144,6 @@ export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent @Inject(APP_CONFIG) public appConfig: AppConfig, public dsoNameService: DSONameService, ) { - super(); this.fetchThumbnails = this.appConfig.browseBy.showThumbnails; this.paginationConfig = Object.assign(new PaginationComponentOptions(), { id: BBM_PAGINATION_ID, @@ -277,7 +291,7 @@ export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent } ngOnDestroy(): void { - super.ngOnDestroy(); + this.subs.filter((sub: Subscription) => hasValue(sub)).forEach((sub: Subscription) => sub.unsubscribe()); this.paginationService.clearPagination(this.paginationConfig.id); } diff --git a/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts b/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts index 3f9271a67d..25483028eb 100644 --- a/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts +++ b/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts @@ -9,7 +9,6 @@ import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { Component } from '@angular/core'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; import { By } from '@angular/platform-browser'; import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; @@ -20,7 +19,7 @@ import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; selector: '', template: '', }) -class BrowseByTestComponent extends AbstractBrowseByTypeComponent { +class BrowseByTestComponent { } class TestBrowseByPageBrowseDefinition extends BrowseDefinition { diff --git a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts index 835c34ebcb..62e666227d 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts @@ -1,6 +1,6 @@ +import { Component } from '@angular/core'; import { hasNoValue } from '../../shared/empty.util'; import { DEFAULT_THEME, resolveTheme } from '../../shared/object-collection/shared/listable-object/listable-object.decorator'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { Context } from '../../core/shared/context.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { BrowseByDataType } from './browse-by-data-type'; @@ -8,7 +8,7 @@ import { BrowseByDataType } from './browse-by-data-type'; export const DEFAULT_BROWSE_BY_TYPE = BrowseByDataType.Metadata; export const DEFAULT_BROWSE_BY_CONTEXT = Context.Any; -const map: Map>>> = new Map(); +const map: Map>>> = new Map(); /** * Decorator used for rendering Browse-By pages by type @@ -41,12 +41,12 @@ export function rendersBrowseBy(browseByType: BrowseByDataType, context = DEFAUL * @param context The context to match * @param theme the theme to match */ -export function getComponentByBrowseByType(browseByType: BrowseByDataType, context: Context, theme: string): GenericConstructor { - let contextMap: Map>> = map.get(browseByType); +export function getComponentByBrowseByType(browseByType: BrowseByDataType, context: Context, theme: string): GenericConstructor { + let contextMap: Map>> = map.get(browseByType); if (hasNoValue(contextMap)) { contextMap = map.get(DEFAULT_BROWSE_BY_TYPE); } - let themeMap: Map> = contextMap.get(context); + let themeMap: Map> = contextMap.get(context); if (hasNoValue(themeMap)) { themeMap = contextMap.get(DEFAULT_BROWSE_BY_CONTEXT); } diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts index 418dfd45e1..65e8c87c61 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts @@ -8,7 +8,6 @@ import { ValueListBrowseDefinition } from '../../core/shared/value-list-browse-d import { NonHierarchicalBrowseDefinition } from '../../core/shared/non-hierarchical-browse-definition'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { DynamicComponentLoaderDirective } from '../../shared/abstract-component-loader/dynamic-component-loader.directive'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from './browse-by-data-type'; @rendersBrowseBy('BrowseBySwitcherComponent' as BrowseByDataType) @@ -17,7 +16,7 @@ import { BrowseByDataType } from './browse-by-data-type'; selector: '', template: '', }) -class BrowseByTestComponent extends AbstractBrowseByTypeComponent { +class BrowseByTestComponent { } describe('BrowseBySwitcherComponent', () => { diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts index 2ebea9a262..0c200c3453 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts @@ -2,28 +2,30 @@ import { Component, Input } from '@angular/core'; import { getComponentByBrowseByType } from './browse-by-decorator'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { AbstractComponentLoaderComponent } from '../../shared/abstract-component-loader/abstract-component-loader.component'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from './browse-by-data-type'; +import { Context } from '../../core/shared/context.model'; @Component({ selector: 'ds-browse-by-switcher', templateUrl: '../../shared/abstract-component-loader/abstract-component-loader.component.html' }) -export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent { +export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent { + + @Input() context: Context; @Input() browseByType: BrowseByDataType; protected inputNamesDependentForComponent: (keyof this & string)[] = [ - ...this.inputNamesDependentForComponent, + 'context', 'browseByType', ]; protected inputNames: (keyof this & string)[] = [ - ...this.inputNames, + 'context', 'browseByType', ]; - public getComponent(): GenericConstructor { + public getComponent(): GenericConstructor { return getComponentByBrowseByType(this.browseByType, this.context, this.themeService.getThemeName()); } diff --git a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts index f005c66c9e..fb2f28c8c5 100644 --- a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts +++ b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts @@ -1,14 +1,14 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model'; import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { map } from 'rxjs/operators'; import { HierarchicalBrowseDefinition } from '../../core/shared/hierarchical-browse-definition.model'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; +import { Context } from '../../core/shared/context.model'; @Component({ selector: 'ds-browse-by-taxonomy-page', @@ -19,7 +19,17 @@ import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; * Component for browsing items by metadata in a hierarchical controlled vocabulary */ @rendersBrowseBy(BrowseByDataType.Hierarchy) -export class BrowseByTaxonomyPageComponent extends AbstractBrowseByTypeComponent implements OnInit, OnDestroy { +export class BrowseByTaxonomyPageComponent implements OnInit, OnDestroy { + + /** + * The optional context + */ + @Input() context: Context; + + /** + * The {@link BrowseByDataType} of this Component + */ + @Input() browseByType: BrowseByDataType; /** * The {@link VocabularyOptions} object @@ -56,10 +66,14 @@ export class BrowseByTaxonomyPageComponent extends AbstractBrowseByTypeComponent */ browseDefinition$: Observable; + /** + * Subscriptions to track + */ + subs: Subscription[] = []; + public constructor( protected route: ActivatedRoute, ) { - super(); } ngOnInit(): void { @@ -108,4 +122,7 @@ export class BrowseByTaxonomyPageComponent extends AbstractBrowseByTypeComponent }; } + ngOnDestroy(): void { + this.subs.forEach((sub: Subscription) => sub.unsubscribe()); + } } diff --git a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts index 888edffe67..2ea7c50d75 100644 --- a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts +++ b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts @@ -1,22 +1,27 @@ -import { Component, ComponentRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; -import { Context } from '../../core/shared/context.model'; +import { Component, ComponentRef, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; import { ThemeService } from '../theme-support/theme.service'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { hasValue, isNotEmpty } from '../empty.util'; import { Subscription } from 'rxjs'; import { DynamicComponentLoaderDirective } from './dynamic-component-loader.directive'; +/** + * To create a new loader component you will need to: + *
    + *
  • Create a new LoaderComponent component extending this component
  • + *
  • Point the templateUrl to this component's templateUrl
  • + *
  • Add all the @Input()/@Output() names that the dynamically generated components should inherit from the loader to the inputNames/outputNames lists
  • + *
  • Create a decorator file containing the new decorator function, a map containing all the collected {@link Component}s and a function to retrieve the {@link Component}
  • + *
  • Call the function to retrieve the correct {@link Component} in getComponent()
  • + *
  • Add all the @Input()s you had to used in getComponent() in the inputNamesDependentForComponent array
  • + *
+ */ @Component({ selector: 'ds-abstract-component-loader', templateUrl: './abstract-component-loader.component.html', }) export abstract class AbstractComponentLoaderComponent implements OnInit, OnChanges, OnDestroy { - /** - * The optional context - */ - @Input() context: Context; - /** * Directive to determine where the dynamic child component is located */ @@ -33,20 +38,21 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC protected subs: Subscription[] = []; /** - * The @{@link Input}() that are used to find the matching component using {@link getComponent}. When the value of - * one of these @{@link Input}() change this loader needs to retrieve the best matching component again using the + * The @Input() that are used to find the matching component using {@link getComponent}. When the value of + * one of these @Input() change this loader needs to retrieve the best matching component again using the * {@link getComponent} method. */ - protected inputNamesDependentForComponent: (keyof this & string)[] = [ - 'context', - ]; + protected inputNamesDependentForComponent: (keyof this & string)[] = []; - protected inputNames: (keyof this & string)[] = [ - 'context', - ]; + /** + * The list of the @Input() names that should be passed down to the dynamically created components. + */ + protected inputNames: (keyof this & string)[] = []; - protected outputNames: (keyof this & string)[] = [ - ]; + /** + * The list of the @Output() names that should be passed down to the dynamically created components. + */ + protected outputNames: (keyof this & string)[] = []; constructor( protected themeService: ThemeService, diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts index 4b63a31a2b..d722d24ff6 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts @@ -19,13 +19,20 @@ import { AbstractComponentLoaderComponent } from '../abstract-component-loader/a */ export class MetadataRepresentationLoaderComponent extends AbstractComponentLoaderComponent { + @Input() context: Context; + /** * The item or metadata to determine the component for */ @Input() mdRepresentation: MetadataRepresentation; + protected inputNamesDependentForComponent: (keyof this & string)[] = [ + 'context', + 'mdRepresentation', + ]; + protected inputNames: (keyof this & string)[] = [ - ...this.inputNames, + 'context', 'mdRepresentation', ]; diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts index fb39347d63..7b01e98249 100644 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts @@ -47,7 +47,6 @@ export class ClaimedTaskActionsLoaderComponent extends AbstractComponentLoaderCo * The list of input and output names for the dynamic component */ protected inputNames: (keyof this & string)[] = [ - ...this.inputNames, 'item', 'object', 'option', @@ -55,7 +54,6 @@ export class ClaimedTaskActionsLoaderComponent extends AbstractComponentLoaderCo ]; protected outputNames: (keyof this & string)[] = [ - ...this.outputNames, 'processCompleted', ]; diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index a83cdfb6b2..d280768631 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -58,7 +58,7 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa /** * Whether to show the thumbnail preview */ - @Input() showThumbnails; + @Input() showThumbnails: boolean; /** * The value to display for this element @@ -70,13 +70,19 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa */ @Output() contentChange = new EventEmitter(); + protected inputNamesDependentForComponent: (keyof this & string)[] = [ + 'object', + 'viewMode', + 'context', + ]; + /** * The list of input and output names for the dynamic component */ protected inputNames: (keyof this & string)[] = [ - ...this.inputNames, 'object', 'index', + 'context', 'linkType', 'listID', 'showLabel', @@ -86,7 +92,6 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa ]; protected outputNames: (keyof this & string)[] = [ - ...this.outputNames, 'contentChange', ];