Skip to content

Commit

Permalink
feat(router): add service for observing route data (#2778)
Browse files Browse the repository at this point in the history
  • Loading branch information
griest024 committed May 15, 2024
1 parent 2a8c77f commit 77d991f
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 0 deletions.
68 changes: 68 additions & 0 deletions libs/router/src/data/helpers/collect-data.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ActivatedRouteSnapshot } from '@angular/router';

import { daffRouterDataCollect } from './collect-data';

describe('@daffodil/router | daffRouterDataCollect', () => {
let route: ActivatedRouteSnapshot;
let result: any;

beforeEach(() => {
route = <ActivatedRouteSnapshot><unknown>{
data: {
root: 'TestClass',
overwrite: 'TestClass',
},
children: [
<ActivatedRouteSnapshot><unknown>{
data: {
'00': 'TestClass',
},
children: [
<ActivatedRouteSnapshot><unknown>{
data: {
10: 'TestClass',
},
children: [],
},
<ActivatedRouteSnapshot><unknown>{
data: {
11: 'TestClass',
overwrite: 'TestClass1',
},
children: [
<ActivatedRouteSnapshot><unknown>{
data: {
20: 'TestClass',
},
children: [],
},
],
},
],
},
<ActivatedRouteSnapshot><unknown>{
data: {
'01': 'TestClass',
},
children: [],
},
],
};

result = daffRouterDataCollect(route);
});

it('should collect all the named views and combine them into a single dict', () => {
expect(result['root']).toBeDefined();
expect(result['00']).toBeDefined();
expect(result['01']).toBeDefined();
expect(result[10]).toBeDefined();
expect(result[11]).toBeDefined();
expect(result[20]).toBeDefined();
expect(result['overwrite']).toBeDefined();
});

it('should give precedence to more deeply nested routes when there is a collision', () => {
expect(result['overwrite']).toEqual('TestClass1');
});
});
24 changes: 24 additions & 0 deletions libs/router/src/data/helpers/collect-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
ActivatedRouteSnapshot,
Route,
} from '@angular/router';

import { collect } from '@daffodil/core';

/**
* Collects data defined in the entire tree of routes.
* Shallow merges data, preferring fields of more deeply nested routes.
*/
export const daffRouterDataCollect = (route: ActivatedRouteSnapshot): Route['data'] => {
const ary = collect(route, (r) => r.children);
const ret = ary.reduce(
(acc, r) => r.data
? {
...acc,
...r.data,
}
: acc,
{},
);
return ret;
};
1 change: 1 addition & 0 deletions libs/router/src/data/helpers/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { daffRouterNamedViewsCollect } from './collect-data';
2 changes: 2 additions & 0 deletions libs/router/src/data/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './helpers/public_api';
export * from './service/data.service';
52 changes: 52 additions & 0 deletions libs/router/src/data/service/data.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Injectable } from '@angular/core';
import {
ActivatedRoute,
NavigationEnd,
Route,
Router,
} from '@angular/router';
import {
Observable,
filter,
map,
merge,
} from 'rxjs';

import { daffRouterDataCollect } from '../helpers/collect-data';

@Injectable({
providedIn: 'root',
})
export class DaffRouterDataService {
/**
* A collection of all the route data defined in any part of the currently activated route's tree.
* Child route's data takes precendence over parent data.
*/
public data$: Observable<Route['data']>;

constructor(
private route: ActivatedRoute,
private router: Router,
) {
/**
* Because data won't reemit for route changes and
* the top-level data probably won't have named views
* anyway, use `url` and router events to listen for route changes
* and pull named views from nested data in the snapshot.
*
* On first page load, this directive will likely not be instantiated
* in time to catch router events so route.url emits for this case.
* On subsequent route changes, `route.url` will not change (why????)
* so we use router events instead.
*/
this.data$ = merge(
this.router.events.pipe(
filter((e) => e instanceof NavigationEnd),
),
this.route.url,
).pipe(
map(() => this.route.snapshot),
map(daffRouterDataCollect),
);
}
}

0 comments on commit 77d991f

Please sign in to comment.