@jftechnology/ember-route-metadata
Version:
Components, services and helpers built on Ember Route Metadata. Breadcrumbs, titles and scrolling for Ember apps.
151 lines (117 loc) • 4.73 kB
JavaScript
import Service, {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';
import Evented from '@ember/object/evented';
import {assign} from '@ember/polyfills';
/**
* Service that presents Ember route info based on metadata content. The service fires events for each metadata
* key provided by any route segment traversed in a settled transition.
*
* @class RouteMetadataService
* @public
*/
export default class RouteMetadataService extends Service.extend(Evented) {
/**
* The injected Ember router service.
*/
router;
/**
* The array of all RouteInfo objects with metadata interpolated from the 'to' property of the last settled route
* transition ordered from root to leaf.
* For example if the settled transition is from A.B.C to A.D.E then this method will return the array of route infos [A,B,C].
*/
previousRouteInfos = [];
/**
* The array of all RouteInfo objects with metadata interpolated from the 'from' property of the last settled route
* transition ordered from root to leaf.
* For example if the settled transition is from A.B.C to A.D.E then this method will return the array of route infos [A,D,E].
*/
currentRouteInfos = [];
/**
* The 'diff' array of all RouteInfo objects with metadata interpolated from the from/to properties of the last
* settled route transition ordered from root to leaf.
* For example if the settled transition is from A.B.C to A.D.E then this method will return the array of route infos [D,E]. This is useful for adding routing behaviours
* such as scrolling that require knowledge of the change in path rather than the absolute settled path.
*
*/
commonRouteInfos = [];
/**
* The 'diff' array of all RouteInfo objects with metadata interpolated from the from/to properties of the last
* settled route transition ordered from root to leaf.
* For example if the settled transition is from A.B.C to A.D.E then this method will return the array of route infos [D,E]. This is useful for adding routing behaviours
* such as scrolling that require knowledge of the change in path rather than the absolute settled path.
*
*/
transitionRouteInfos = [];
constructor() {
super(...arguments);
this.router.on('routeDidChange', (transition) => this.onTransition(transition?.from, transition?.to));
this.onTransition(undefined, this.router.currentRoute);
}
/**
* Observer method that calculates all route segments traversed by a transition, aggregates any metadata keys in the
* traversal path, and fires an event ('metadata.[key]') for each key.
*/
onTransition(from, to) {
this.currentRouteInfos = _filterForMetadata(to);
this.previousRouteInfos = _filterForMetadata(from);
this.transitionRouteInfos = this.currentRouteInfos.filter((m, index) => {
if (this.previousRouteInfos.length <= index) {
return true;
}
return m.name !== this.previousRouteInfos[index].name;
});
this.commonRouteInfos = this.currentRouteInfos.filter((m, index) => {
if (this.previousRouteInfos.length <= index) {
return false;
}
return m.name === this.previousRouteInfos[index].name;
});
let fromMetadata = this.previousRouteInfos.slice(this.commonRouteInfos.length).map(r => r.metadata);
let toMetadata = this.currentRouteInfos.slice(this.commonRouteInfos.length).map(r => r.metadata);
let metadataTraversal = {};
fromMetadata.forEach(m => assign(metadataTraversal, m));
toMetadata.forEach(m => assign(metadataTraversal, m));
this.trigger('didTransition');
for (const key in metadataTraversal) {
this.trigger(`metadata.${key}`, key, this.transitionRouteInfos.map(ri => ri.metadata));
}
}
/**
* @method findPreviousMetadata
* @param {String} key The metadata to search for.
*/
findPreviousRouteInfos(key) {
return this.previousRouteInfos.filter(keyFilter(key));
}
/**
* @method findCurrentMetadata
* @param {String} key The metadata to search for.
*/
findCurrentRouteInfos(key) {
return this.currentRouteInfos.filter(keyFilter(key));
}
/**
* @method findTransitionMetadata
* @param {String} key The metadata to search for.
*/
findTransitionRouteInfos(key) {
return this.transitionRouteInfos.filter(keyFilter(key));
}
}
function keyFilter(key) {
return (routeInfo) => routeInfo.metadata ? routeInfo.metadata[key] : false;
}
function _filterForMetadata(route) {
let routeInfos = [];
if (route) {
route.find(info => {
routeInfos.push(info);
});
}
return routeInfos.filter(info => info.metadata);
}