All files route-decorators.ts

100% Statements 38/38
100% Branches 18/18
100% Functions 10/10
100% Lines 33/33
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99  1x 1x                     134x   134x 402x 402x     134x                           766x 199x 766x 1016x 92x       766x     199x     146x 80x   66x                                 1x 239x 155x   155x 155x   155x 50x     155x 148x 2x     146x   146x 245x     146x 146x 132x            
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { combineLatest } from 'rxjs/observable/combineLatest';
 
/**
 * Traverses the routes, from the current route all the way up to the
 * root route and stores the for each route data, params or queryParams observable
 *
 * @param {ActivatedRoute} parent
 * @param {string} routeProperty
 * @returns {Observable<Data | Params>[]}
 */
function extractRoutes(parent, routeProperty): Observable<any>[] {
    let routes = [];
 
    while (parent) {
        routes.push(parent[routeProperty]);
        parent = parent.parent;
    }
 
    return routes;
}
 
/**
 * Merge all observables from {@link extractRoutes} into a single stream passing through only the data/params
 * of decorator. Depending on how the decorator was initialized (`{observable: false}`) the observable or the actual
 * values are passed into the callback.
 *
 * @param {Observable<Data | Params>[]} routes
 * @param {string[]} args list of the decorator's arguments
 * @param {RouteConfigXxl} config the decorator's configuration object
 * @param {(Observable<any> | any) => void} cb callback function receiving the final observable or the actual values as its arguments
 */
function extractValues(routes, args, config, cb): void {
    const stream$ = combineLatest(of(null), ...routes, (...routeValues) => {
            const values = routeValues.reduce((obj, route) => {
                args.forEach(arg => {
                    if (route && route[arg] !== undefined) {
                        obj[arg] = route[arg];
                    }
                });
 
                return obj;
            }, {});
 
            return args.length === 1 ? values[args[0]] : values;
        });
 
    if (config.observable === false) {
        stream$.subscribe(cb);
    } else {
        cb(stream$);
    }
}
 
/**
 * The configuration interface used to configure the decorators
 */
export interface RouteXxlConfig {
    observable?: boolean;
}
 
/**
 * Factory function which creates decorators for resolved route data, route params or query parameters.
 *
 * @param {string} routeProperty used to create a data, params or queryParams decorator function
 * @returns {(...args: string | RouteXxlConfig[]) => any}
 */
export function routeDecoratorFactory(routeProperty) {
    return function (...args: Array<string | RouteXxlConfig>): any {
        const config = (typeof args[args.length - 1] === 'object' ? args.pop() : {}) as RouteXxlConfig;
 
        return (target: any, key: string, index: number): void => {
            const ngOnInit = target.ngOnInit;
 
            if (!args.length) {
                args = [key.replace(/\$$/, '')];
            }
 
            target.ngOnInit = function (): void {
                if (!this.route) {
                    throw(`${target.constructor.name} uses the ${routeProperty} @decorator without a 'route' property`);
                }
 
                const routes = routeProperty === 'queryParams' ? [this.route.queryParams] : extractRoutes(this.route, routeProperty);
 
                extractValues(routes, args, config, values => {
                    this[key] = values;
                });
 
                this.ngOnInit = ngOnInit;
                if (ngOnInit) {
                    this.ngOnInit();
                }
            };
        };
    };
}