diff --git a/src/app/admin/admin-ldn-services/admin-ldn-services-routing.module.ts b/src/app/admin/admin-ldn-services/admin-ldn-services-routing.module.ts index 300daa7045..c94083d3ba 100644 --- a/src/app/admin/admin-ldn-services/admin-ldn-services-routing.module.ts +++ b/src/app/admin/admin-ldn-services/admin-ldn-services-routing.module.ts @@ -1,32 +1,45 @@ -import {NgModule} from '@angular/core'; -import {RouterModule} from '@angular/router'; -import {I18nBreadcrumbResolver} from 'src/app/core/breadcrumbs/i18n-breadcrumb.resolver'; -import {LdnServicesOverviewComponent} from './ldn-services-directory/ldn-services-directory.component'; -import {LdnServiceFormComponent} from './ldn-service-form/ldn-service-form.component'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { LdnServicesOverviewComponent } from './ldn-services-directory/ldn-services-directory.component'; +import { NavigationBreadcrumbResolver } from '../../core/breadcrumbs/navigation-breadcrumb.resolver'; +import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver'; +import { LdnServiceFormComponent } from './ldn-service-form/ldn-service-form.component'; + + +const moduleRoutes: Routes = [ + { + path: '', + pathMatch: 'full', + component: LdnServicesOverviewComponent, + resolve: {breadcrumb: I18nBreadcrumbResolver}, + data: {title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new'}, + }, + { + path: 'new', + resolve: {breadcrumb: NavigationBreadcrumbResolver}, + component: LdnServiceFormComponent, + data: {title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service'} + }, + { + path: 'edit/:serviceId', + resolve: {breadcrumb: NavigationBreadcrumbResolver}, + component: LdnServiceFormComponent, + data: {title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service'} + }, +]; + @NgModule({ imports: [ - RouterModule.forChild([ - { - path: '', - pathMatch: 'full', - component: LdnServicesOverviewComponent, - resolve: {breadcrumb: I18nBreadcrumbResolver}, - data: {title: 'ldn-registered-services.title', breadcrumbKey: 'ldn-registered-services.new'}, - }, - { - path: 'new', - resolve: {breadcrumb: I18nBreadcrumbResolver}, - component: LdnServiceFormComponent, - data: {title: 'ldn-register-new-service.title', breadcrumbKey: 'ldn-register-new-service'} - }, - { - path: 'edit/:serviceId', - resolve: {breadcrumb: I18nBreadcrumbResolver}, - component: LdnServiceFormComponent, - data: {title: 'ldn-edit-service.title', breadcrumbKey: 'ldn-edit-service'} - }, - ]), + RouterModule.forChild(moduleRoutes.map(route => { + return {...route, data: { + ...route.data, + relatedRoutes: moduleRoutes.filter(relatedRoute => relatedRoute.path !== route.path) + .map((relatedRoute) => { + return {path: relatedRoute.path, data: relatedRoute.data}; + }) + }}; + })) ] }) export class AdminLdnServicesRoutingModule { diff --git a/src/app/admin/admin-ldn-services/admin-ldn-services.module.ts b/src/app/admin/admin-ldn-services/admin-ldn-services.module.ts index 1eecf1d751..45ec696cd3 100644 --- a/src/app/admin/admin-ldn-services/admin-ldn-services.module.ts +++ b/src/app/admin/admin-ldn-services/admin-ldn-services.module.ts @@ -1,11 +1,11 @@ -import {NgModule} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {AdminLdnServicesRoutingModule} from './admin-ldn-services-routing.module'; -import {LdnServicesOverviewComponent} from './ldn-services-directory/ldn-services-directory.component'; -import {SharedModule} from '../../shared/shared.module'; -import {LdnServiceFormComponent} from './ldn-service-form/ldn-service-form.component'; -import {FormsModule} from '@angular/forms'; -import {LdnItemfiltersService} from './ldn-services-data/ldn-itemfilters-data.service'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { AdminLdnServicesRoutingModule } from './admin-ldn-services-routing.module'; +import { LdnServicesOverviewComponent } from './ldn-services-directory/ldn-services-directory.component'; +import { SharedModule } from '../../shared/shared.module'; +import { LdnServiceFormComponent } from './ldn-service-form/ldn-service-form.component'; +import { FormsModule } from '@angular/forms'; +import { LdnItemfiltersService } from './ldn-services-data/ldn-itemfilters-data.service'; @NgModule({ diff --git a/src/app/core/breadcrumbs/navigation-breadcrumb.resolver.spec.ts b/src/app/core/breadcrumbs/navigation-breadcrumb.resolver.spec.ts new file mode 100644 index 0000000000..81b3b8ad2a --- /dev/null +++ b/src/app/core/breadcrumbs/navigation-breadcrumb.resolver.spec.ts @@ -0,0 +1,52 @@ +import { NavigationBreadcrumbResolver } from './navigation-breadcrumb.resolver'; + +describe('NavigationBreadcrumbResolver', () => { + describe('resolve', () => { + let resolver: NavigationBreadcrumbResolver; + let NavigationBreadcrumbService: any; + let i18nKey: string; + let relatedI18nKey: string; + let route: any; + let expectedPath; + let state; + beforeEach(() => { + i18nKey = 'example.key'; + relatedI18nKey = 'related.key'; + route = { + data: { + breadcrumbKey: i18nKey, + relatedRoutes: [ + { + path: '', + data: {breadcrumbKey: relatedI18nKey}, + } + ] + }, + routeConfig: { + path: 'example' + }, + parent: { + routeConfig: { + path: '' + }, + url: [{ + path: 'base' + }] + } as any + }; + + state = { + url: '/base/example' + }; + expectedPath = '/base/example:/base'; + NavigationBreadcrumbService = {}; + resolver = new NavigationBreadcrumbResolver(NavigationBreadcrumbService); + }); + + it('should resolve the breadcrumb config', () => { + const resolvedConfig = resolver.resolve(route, state); + const expectedConfig = { provider: NavigationBreadcrumbService, key: `${i18nKey}:${relatedI18nKey}`, url: expectedPath }; + expect(resolvedConfig).toEqual(expectedConfig); + }); + }); +}); diff --git a/src/app/core/breadcrumbs/navigation-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/navigation-breadcrumb.resolver.ts new file mode 100644 index 0000000000..18ebfc395b --- /dev/null +++ b/src/app/core/breadcrumbs/navigation-breadcrumb.resolver.ts @@ -0,0 +1,52 @@ +import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model'; +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; +import { NavigationBreadcrumbsService } from './navigation-breadcrumb.service'; + +/** + * The class that resolves a BreadcrumbConfig object with an i18n key string for a route and related parents + */ +@Injectable({ + providedIn: 'root' +}) +export class NavigationBreadcrumbResolver implements Resolve> { + + private parentRoutes: ActivatedRouteSnapshot[] = []; + constructor(protected breadcrumbService: NavigationBreadcrumbsService) { + } + + /** + * Method to collect all parent routes snapshot from current route snapshot + * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot + */ + private getParentRoutes(route: ActivatedRouteSnapshot): void { + if (route.parent) { + this.parentRoutes.push(route.parent); + this.getParentRoutes(route.parent); + } + } + /** + * Method for resolving an I18n breadcrumb configuration object + * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot + * @param {RouterStateSnapshot} state The current RouterStateSnapshot + * @returns BreadcrumbConfig object + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): BreadcrumbConfig { + this.getParentRoutes(route); + const relatedRoutes = route.data.relatedRoutes; + const parentPaths = this.parentRoutes.map(parent => parent.routeConfig?.path); + const relatedParentRoutes = relatedRoutes.filter(relatedRoute => parentPaths.includes(relatedRoute.path)); + const baseUrlSegmentPath = route.parent.url[route.parent.url.length - 1].path; + const baseUrl = state.url.substring(0, state.url.lastIndexOf(baseUrlSegmentPath) + baseUrlSegmentPath.length); + + + const combinedParentBreadcrumbKeys = relatedParentRoutes.reduce((previous, current) => { + return `${previous}:${current.data.breadcrumbKey}`; + }, route.data.breadcrumbKey); + const combinedUrls = relatedParentRoutes.reduce((previous, current) => { + return `${previous}:${baseUrl}${current.path}`; + }, state.url); + + return {provider: this.breadcrumbService, key: combinedParentBreadcrumbKeys, url: combinedUrls}; + } +} diff --git a/src/app/core/breadcrumbs/navigation-breadcrumb.service.ts b/src/app/core/breadcrumbs/navigation-breadcrumb.service.ts new file mode 100644 index 0000000000..beebeed94e --- /dev/null +++ b/src/app/core/breadcrumbs/navigation-breadcrumb.service.ts @@ -0,0 +1,30 @@ +import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; +import { BreadcrumbsProviderService } from './breadcrumbsProviderService'; +import { Observable, of as observableOf } from 'rxjs'; +import { Injectable } from '@angular/core'; + +/** + * The postfix for i18n breadcrumbs + */ +export const BREADCRUMB_MESSAGE_POSTFIX = '.breadcrumbs'; + +/** + * Service to calculate i18n breadcrumbs for a single part of the route + */ +@Injectable({ + providedIn: 'root' +}) +export class NavigationBreadcrumbsService implements BreadcrumbsProviderService { + + /** + * Method to calculate the breadcrumbs + * @param key The key used to resolve the breadcrumb + * @param url The url to use as a link for this breadcrumb + */ + getBreadcrumbs(key: string, url: string): Observable { + const keys = key.split(':'); + const urls = url.split(':'); + const breadcrumbs = keys.map((currentKey, index) => new Breadcrumb(currentKey + BREADCRUMB_MESSAGE_POSTFIX, urls[index] )); + return observableOf(breadcrumbs.reverse()); + } +} diff --git a/src/app/core/breadcrumbs/navigation-breadcrumbs.service.spec.ts b/src/app/core/breadcrumbs/navigation-breadcrumbs.service.spec.ts new file mode 100644 index 0000000000..98e20e285d --- /dev/null +++ b/src/app/core/breadcrumbs/navigation-breadcrumbs.service.spec.ts @@ -0,0 +1,43 @@ +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; +import { getTestScheduler } from 'jasmine-marbles'; +import { BREADCRUMB_MESSAGE_POSTFIX } from './i18n-breadcrumbs.service'; +import { NavigationBreadcrumbsService } from './navigation-breadcrumb.service'; + +describe('NavigationBreadcrumbsService', () => { + let service: NavigationBreadcrumbsService; + let exampleString; + let exampleURL; + let childrenString; + let childrenUrl; + let parentString; + let parentUrl; + + function init() { + exampleString = 'example.string:parent.string'; + exampleURL = 'example.com:parent.com'; + childrenString = 'example.string'; + childrenUrl = 'example.com'; + parentString = 'parent.string'; + parentUrl = 'parent.com'; + } + + beforeEach(waitForAsync(() => { + init(); + TestBed.configureTestingModule({}).compileComponents(); + })); + + beforeEach(() => { + service = new NavigationBreadcrumbsService(); + }); + + describe('getBreadcrumbs', () => { + it('should return an array of breadcrumbs based on strings by adding the postfix', () => { + const breadcrumbs = service.getBreadcrumbs(exampleString, exampleURL); + getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: [ + new Breadcrumb(childrenString + BREADCRUMB_MESSAGE_POSTFIX, childrenUrl), + new Breadcrumb(parentString + BREADCRUMB_MESSAGE_POSTFIX, parentUrl), + ].reverse() }); + }); + }); +});