source : resourceUrlTypeFactoryService.js

/**
 * Angular ResourceUrlTypeFactoryService
 * Copyright 2016 Andreas Stocker
 * MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
 * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
(function () {
    'use strict';
    var
        module = angular.module('ngResourceUrlTypeFactory');
    /**
     * Provider for the ResourceUrlTypeFactoryService
     *
     * @name ResourceUrlTypeFactoryServiceProvider
     * @ngdoc provider
     */
    module.provider('ResourceUrlTypeFactoryService',
        function ($urlMatcherFactoryProvider) {
            'ngInject';
            var
                provider = this,
                /**
                 * List of REST type names
                 * @type {Array}
                 */
                typeNames = [];
            /**
             * Construct a type definition for a REST ui-router type
             * @memberOf ResourceUrlTypeFactoryServiceProvider
             * @param {String} typeName Name for the type
             * @param {RegExp} pattern Pattern to match for
             * @param {String} pkAttr Name of the PK attribute
             * @param {Function} fetchFn Function to fetch the object from REST API (supports injection)
             */
            provider.registerType = function (typeName, pattern, pkAttr, fetchFn) {
                console.log("ResourceUrlTypeFactory: Register '" + typeName + "' REST type for ui-router.");
                /*
                 * Register the URL pattern type itself. It will convert a REST object to
                 * it's URL representation, and will invoke the given fetch function to
                 * load a REST object from it's URL representation.
                 */
                $urlMatcherFactoryProvider.type(typeName,
                    {
                        pattern: pattern
                    },
                    function ($injector) {
                        'ngInject';
                        return {
                            encode: function (obj) {
                                return String(obj[pkAttr]);
                            },
                            decode: function (pk) {
                                var
                                    promise = $injector.invoke(fetchFn, null, {'$pk': pk});
                                promise[pkAttr] = pk;
                                return promise;
                            },
                            equals: function (objA, objB) {
                                if (objA && objB) {
                                    return String(objA[pkAttr]).toUpperCase() === String(objB[pkAttr]).toUpperCase();
                                }
                                return !objA && !objB;
                            },
                            is: function (obj) {
                                return angular.isObject(obj);
                            }
                        }
                    }
                );
                typeNames.push(typeName);
            };
            /**
             * Gets the actual service object
             * @name ResourceUrlTypeFactoryService
             * @ngdoc factory
             * @returns {Object}
             */
            provider.$get = function ($q, $transitions) {
                'ngInject';
                var
                    self = this,
                    /**
                     * Get a promise of the given object. Returns the object itself if we pass a promise, returns
                     * the `$promise` attribute if we pass a resource instance, and returns a resolved promise for the
                     * passed object on anything else.
                     * @private
                     * @memberOf ResourceUrlTypeFactoryService
                     * @param obj
                     * @return {Promise}
                     */
                    getPromise = function (obj) {
                        if (obj && (typeof obj.then === 'function')) {
                            return obj;
                        }
                        else if (obj && obj.$promise && (typeof obj.$promise.then === 'function')) {
                            return obj.$promise;
                        }
                        else {
                            return $q.resolve(obj);
                        }
                    };
                /**
                 * Returns a list of names for all registered types.
                 * @memberOf ResourceUrlTypeFactoryService
                 * @return {String[]}
                 */
                self.getTypeNames = function () {
                    return angular.copy(typeNames);
                };
                /**
                 * Gets the param names that are resource url type factory types for the
                 * given param definitions.
                 * @memberOf ResourceUrlTypeFactoryService
                 * @param paramDefinitions
                 * @return {String[]}
                 */
                self.getParams = function (paramDefinitions) {
                    var
                        paramNames = [];
                    for (var paramName in paramDefinitions) {
                        if (paramDefinitions.hasOwnProperty(paramName)) {
                            var
                                param = paramDefinitions[paramName],
                                paramType = param.type,
                                paramTypeName = paramType.name;
                            if (typeNames.indexOf(paramTypeName) !== -1) {
                                paramNames.push(paramName);
                            }
                        }
                    }
                    return paramNames;
                };
                /**
                 * Registers a ui-router transition handler for the registered types above. The transition
                 * will wait for the promises to resolve before the routing finishes.
                 * @memberOf ResourceUrlTypeFactoryService
                 */
                self.registerTransition = function () {
                    $transitions.onStart(
                        {
                            to: function (state) {
                                return self.getParams(state.params).length !== 0;
                            }
                        },
                        function transition (trans) {
                            var
                                targetState = trans._targetState,
                                paramDefinitions = targetState._definition.params,
                                paramNames = self.getParams(paramDefinitions),
                                promises = [];
                            for (var i = 0; i < paramNames.length; i++) {
                                var
                                    paramName = paramNames[i],
                                    param = targetState._params[paramName],
                                    promise = getPromise(param);
                                promises.push(promise);
                            }
                            console.log("ResourceUrlTypeFactory: State transition waits for '" + promises.length + "' calls");
                            return $q.all(promises);
                        }
                    );
                };
                return self;
            };
        }
    );
    module.run(
        function (ResourceUrlTypeFactoryService) {
            'ngInject';
            console.log("ResourceUrlTypeFactory: Register transition for ui-router resource types");
            ResourceUrlTypeFactoryService.registerTransition();
        }
    )
})();