UNPKG

ui-router

Version:

State-based routing for Javascript

279 lines (243 loc) 10.6 kB
/** * Provides implementation of the UI-Router 0.2.x state events. * * The 0.2.x state events are deprecated. We recommend moving to Transition Hooks instead, as they * provide much more flexibility, support async, and provide the context (the Transition, etc) necessary * to implement meaningful application behaviors. * * To enable these state events, include the `stateEvents.js` file in your project, e.g., * ``` * <script src="stateEvents.js"></script> * ``` * and also make sure you depend on the `ui.router.state.events` angular module, e.g., * ``` * angular.module("myApplication", ['ui.router', 'ui.router.state.events'] * ``` * * @module ng1_state_events */ /** */ import {IServiceProviderFactory} from "angular"; import {TargetState, StateService, StateProvider} from "../state/module"; import {Transition} from "../transition/transition"; /** * An event broadcast on `$rootScope` when the state transition **begins**. * * You can use `event.preventDefault()` * to prevent the transition from happening and then the transition promise will be * rejected with a `'transition prevented'` value. * * Additional arguments to the event handler are provided: * - `toState`: the Transition Target state * - `toParams`: the Transition Target Params * - `fromState`: the state the transition is coming from * - `fromParams`: the parameters from the state the transition is coming from * - `options`: any Transition Options * - `$transition$`: the [[Transition]] * * @example * ``` * * $rootScope.$on('$stateChangeStart', function(event, transition) { * event.preventDefault(); * // transitionTo() promise will be rejected with * // a 'transition prevented' error * }) * ``` * * @deprecated use [[TransitionService.onStart]] * @event $stateChangeStart */ var $stateChangeStart; /** * An event broadcast on `$rootScope` if a transition is **cancelled**. * * Additional arguments to the event handler are provided: * - `toState`: the Transition Target state * - `toParams`: the Transition Target Params * - `fromState`: the state the transition is coming from * - `fromParams`: the parameters from the state the transition is coming from * - `options`: any Transition Options * - `$transition$`: the [[Transition]] that was cancelled * * @deprecated * @event $stateChangeCancel */ var $stateChangeCancel; /** * * An event broadcast on `$rootScope` once the state transition is **complete**. * * Additional arguments to the event handler are provided: * - `toState`: the Transition Target state * - `toParams`: the Transition Target Params * - `fromState`: the state the transition is coming from * - `fromParams`: the parameters from the state the transition is coming from * - `options`: any Transition Options * - `$transition$`: the [[Transition]] that just succeeded * * @deprecated use [[TransitionService.onStart]] and [[Transition.promise]], or [[Transition.onSuccess]] * @event $stateChangeSuccess */ var $stateChangeSuccess; /** * An event broadcast on `$rootScope` when an **error occurs** during transition. * * It's important to note that if you * have any errors in your resolve functions (javascript errors, non-existent services, etc) * they will not throw traditionally. You must listen for this $stateChangeError event to * catch **ALL** errors. * * Additional arguments to the event handler are provided: * - `toState`: the Transition Target state * - `toParams`: the Transition Target Params * - `fromState`: the state the transition is coming from * - `fromParams`: the parameters from the state the transition is coming from * - `error`: The reason the transition errored. * - `options`: any Transition Options * - `$transition$`: the [[Transition]] that errored * * @deprecated use [[TransitionService.onStart]] and [[Transition.promise]], or [[Transition.onError]] * @event $stateChangeError */ var $stateChangeError; /** * An event broadcast on `$rootScope` when a requested state **cannot be found** using the provided state name. * * The event is broadcast allowing any handlers a single chance to deal with the error (usually by * lazy-loading the unfound state). A `TargetState` object is passed to the listener handler, * you can see its properties in the example. You can use `event.preventDefault()` to abort the * transition and the promise returned from `transitionTo()` will be rejected with a * `'transition aborted'` error. * * Additional arguments to the event handler are provided: * - `unfoundState` Unfound State information. Contains: `to, toParams, options` properties. * - `fromState`: the state the transition is coming from * - `fromParams`: the parameters from the state the transition is coming from * - `options`: any Transition Options * @example * * <pre> * // somewhere, assume lazy.state has not been defined * $state.go("lazy.state", { a: 1, b: 2 }, { inherit: false }); * * // somewhere else * $scope.$on('$stateNotFound', function(event, transition) { * function(event, unfoundState, fromState, fromParams){ * console.log(unfoundState.to); // "lazy.state" * console.log(unfoundState.toParams); // {a:1, b:2} * console.log(unfoundState.options); // {inherit:false} + default options * }); * </pre> * * @deprecated use [[StateProvider.onInvalid]] // TODO: Move to [[StateService.onInvalid]] * @event $stateNotFound */ var $stateNotFound; (function() { let {isFunction, isString} = angular; function applyPairs(memo, keyValTuple: any[]) { let key, value; if (Array.isArray(keyValTuple)) [key, value] = keyValTuple; if (!isString(key)) throw new Error("invalid parameters to applyPairs"); memo[key] = value; return memo; } stateChangeStartHandler.$inject = ['$transition$', '$stateEvents', '$rootScope', '$state', '$urlRouter']; function stateChangeStartHandler($transition$: Transition, $stateEvents, $rootScope, $state, $urlRouter) { if (!$transition$.options().notify || !$transition$.valid() || $transition$.ignored()) return; let enabledEvents = $stateEvents.provider.enabled(); let toParams = $transition$.params("to"); let fromParams = $transition$.params("from"); if (enabledEvents.$stateChangeSuccess) { let startEvent = $rootScope.$broadcast('$stateChangeStart', $transition$.to(), toParams, $transition$.from(), fromParams, $transition$.options(), $transition$); if (startEvent.defaultPrevented) { if (enabledEvents.$stateChangeCancel) { $rootScope.$broadcast('$stateChangeCancel', $transition$.to(), toParams, $transition$.from(), fromParams, $transition$.options(), $transition$); } //Don't update and resync url if there's been a new transition started. see issue #2238, #600 if ($state.transition == null) $urlRouter.update(); return false; } $transition$.promise.then(function () { $rootScope.$broadcast('$stateChangeSuccess', $transition$.to(), toParams, $transition$.from(), fromParams, $transition$.options(), $transition$); }); } if (enabledEvents.$stateChangeError) { $transition$.promise["catch"](function (error) { if (error && (error.type === 2 /* RejectType.SUPERSEDED */ || error.type === 3 /* RejectType.ABORTED */)) return; let evt = $rootScope.$broadcast('$stateChangeError', $transition$.to(), toParams, $transition$.from(), fromParams, error, $transition$.options(), $transition$); if (!evt.defaultPrevented) { $urlRouter.update(); } }); } } stateNotFoundHandler.$inject = ['$to$', '$from$', '$state', '$rootScope', '$urlRouter']; function stateNotFoundHandler($to$: TargetState, $from$: TargetState, $state: StateService, $rootScope, $urlRouter) { let redirect = {to: $to$.identifier(), toParams: $to$.params(), options: $to$.options()}; let e = $rootScope.$broadcast('$stateNotFound', redirect, $from$.state(), $from$.params()); if (e.defaultPrevented || e.retry) $urlRouter.update(); function redirectFn(): TargetState { return $state.target(redirect.to, redirect.toParams, redirect.options); } if (e.defaultPrevented) { return false; } else if (e.retry || !!$state.get(redirect.to)) { return e.retry && isFunction(e.retry.then) ? e.retry.then(redirectFn) : redirectFn(); } } $StateEventsProvider.$inject = ['$stateProvider']; function $StateEventsProvider($stateProvider: StateProvider) { $StateEventsProvider.prototype.instance = this; interface IEventsToggle { $stateChangeStart: boolean; $stateNotFound: boolean; $stateChangeSuccess: boolean; $stateChangeError: boolean; } let runtime = false; let allEvents = ['$stateChangeStart', '$stateNotFound', '$stateChangeSuccess', '$stateChangeError']; let enabledStateEvents = <IEventsToggle> allEvents.map(e => [e, true]).reduce(applyPairs, {}); function assertNotRuntime() { if (runtime) throw new Error("Cannot enable events at runtime (use $stateEventsProvider"); } /** * Enables the deprecated UI-Router 0.2.x State Events * [ '$stateChangeStart', '$stateNotFound', '$stateChangeSuccess', '$stateChangeError' ] */ this.enable = function (...events: string[]) { assertNotRuntime(); if (!events || !events.length) events = allEvents; events.forEach(event => enabledStateEvents[event] = true); }; /** * Disables the deprecated UI-Router 0.2.x State Events * [ '$stateChangeStart', '$stateNotFound', '$stateChangeSuccess', '$stateChangeError' ] */ this.disable = function (...events: string[]) { assertNotRuntime(); if (!events || !events.length) events = allEvents; events.forEach(event => delete enabledStateEvents[event]); }; this.enabled = () => enabledStateEvents; this.$get = $get; $get.$inject = ['$transitions']; function $get($transitions) { runtime = true; if (enabledStateEvents["$stateNotFound"]) $stateProvider.onInvalid(stateNotFoundHandler); if (enabledStateEvents.$stateChangeStart) $transitions.onBefore({}, stateChangeStartHandler, {priority: 1000}); return { provider: $StateEventsProvider.prototype.instance }; } } angular.module('ui.router.state.events', ['ui.router.state']) .provider("$stateEvents", <IServiceProviderFactory> $StateEventsProvider) .run(['$stateEvents', function ($stateEvents) { /* Invokes $get() */ }]); })();