All files route-decorators.ts

100% Statements 52/52
100% Branches 18/18
100% Functions 13/13
100% Lines 41/41
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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117  1x 1x                     134x   134x 402x 402x     134x                           766x 199x 766x 1016x 92x       766x     199x     146x 80x   66x                                   239x 155x   155x 155x   155x 50x     155x 148x 2x     146x   146x 245x     146x 146x 132x             108x 69x   69x     108x 69x   69x     23x 17x   17x    
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>[] {
    const 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}
 */
function routeDecoratorFactory(routeProperty) {
    return (...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();
                }
            };
        };
    };
}
 
export function RouteData(...args: Array<string | RouteXxlConfig>): any {
    const handler = routeDecoratorFactory('data');
 
    return handler(...args);
}
 
export function RouteParams(...args: Array<string | RouteXxlConfig>): any {
    const handler = routeDecoratorFactory('params');
 
    return handler(...args);
}
 
export function RouteQueryParams(...args: Array<string | RouteXxlConfig>): any {
    const handler = routeDecoratorFactory('queryParams');
 
    return handler(...args);
}