Skip to content

Commit

Permalink
feat(*): Create router.dispose() to dispose a router instance and res…
Browse files Browse the repository at this point in the history
…ources.
  • Loading branch information
christopherthielen committed Dec 2, 2016
1 parent 31ac53c commit 0690917
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 37 deletions.
4 changes: 4 additions & 0 deletions src/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ export const removeFrom = curry((array: any[], obj: any) => {
return array;
});

/** pushes a values to an array and returns the value */
export const pushTo = <T> (arr: T[], val: T) =>
(arr.push(val), val);

/**
* Applies a set of defaults to an options object. The options object is filtered
* to only those properties of the objects in the defaultsList.
Expand Down
26 changes: 13 additions & 13 deletions src/common/trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ export class Trace {
}

/** @internalapi called by ui-router code */
traceTransitionStart(transition: Transition) {
traceTransitionStart(trans: Transition) {
if (!this.enabled(Category.TRANSITION)) return;
let tid = transition.$id,
let tid = trans.$id,
digest = this.approximateDigests,
transitionStr = stringify(transition);
console.log(`Transition #${tid} Digest #${digest}: Started -> ${transitionStr}`);
transitionStr = stringify(trans);
console.log(`Transition #${tid} r${trans.router.$id}: Started -> ${transitionStr}`);
}

/** @internalapi called by ui-router code */
Expand All @@ -158,27 +158,27 @@ export class Trace {
let tid = trans && trans.$id,
digest = this.approximateDigests,
transitionStr = stringify(trans);
console.log(`Transition #${tid} Digest #${digest}: Ignored <> ${transitionStr}`);
console.log(`Transition #${tid} r${trans.router.$id}: Ignored <> ${transitionStr}`);
}

/** @internalapi called by ui-router code */
traceHookInvocation(step: TransitionHook, options: any) {
traceHookInvocation(step: TransitionHook, trans: Transition, options: any) {
if (!this.enabled(Category.HOOK)) return;
let tid = parse("transition.$id")(options),
digest = this.approximateDigests,
event = parse("traceData.hookType")(options) || "internal",
context = parse("traceData.context.state.name")(options) || parse("traceData.context")(options) || "unknown",
name = functionToString((step as any).registeredHook.callback);
console.log(`Transition #${tid} Digest #${digest}: Hook -> ${event} context: ${context}, ${maxLength(200, name)}`);
console.log(`Transition #${tid} r${trans.router.$id}: Hook -> ${event} context: ${context}, ${maxLength(200, name)}`);
}

/** @internalapi called by ui-router code */
traceHookResult(hookResult: HookResult, transitionOptions: any) {
traceHookResult(hookResult: HookResult, trans: Transition, transitionOptions: any) {
if (!this.enabled(Category.HOOK)) return;
let tid = parse("transition.$id")(transitionOptions),
digest = this.approximateDigests,
hookResultStr = stringify(hookResult);
console.log(`Transition #${tid} Digest #${digest}: <- Hook returned: ${maxLength(200, hookResultStr)}`);
console.log(`Transition #${tid} r${trans.router.$id}: <- Hook returned: ${maxLength(200, hookResultStr)}`);
}

/** @internalapi called by ui-router code */
Expand All @@ -187,7 +187,7 @@ export class Trace {
let tid = trans && trans.$id,
digest = this.approximateDigests,
pathStr = path && path.toString();
console.log(`Transition #${tid} Digest #${digest}: Resolving ${pathStr} (${when})`);
console.log(`Transition #${tid} r${trans.router.$id}: Resolving ${pathStr} (${when})`);
}

/** @internalapi called by ui-router code */
Expand All @@ -197,7 +197,7 @@ export class Trace {
digest = this.approximateDigests,
resolvableStr = resolvable && resolvable.toString(),
result = stringify(resolvable.data);
console.log(`Transition #${tid} Digest #${digest}: <- Resolved ${resolvableStr} to: ${maxLength(200, result)}`);
console.log(`Transition #${tid} r${trans.router.$id}: <- Resolved ${resolvableStr} to: ${maxLength(200, result)}`);
}

/** @internalapi called by ui-router code */
Expand All @@ -206,7 +206,7 @@ export class Trace {
let tid = trans && trans.$id,
digest = this.approximateDigests,
transitionStr = stringify(trans);
console.log(`Transition #${tid} Digest #${digest}: <- Rejected ${transitionStr}, reason: ${reason}`);
console.log(`Transition #${tid} r${trans.router.$id}: <- Rejected ${transitionStr}, reason: ${reason}`);
}

/** @internalapi called by ui-router code */
Expand All @@ -216,7 +216,7 @@ export class Trace {
digest = this.approximateDigests,
state = finalState.name,
transitionStr = stringify(trans);
console.log(`Transition #${tid} Digest #${digest}: <- Success ${transitionStr}, final state: ${state}`);
console.log(`Transition #${tid} r${trans.router.$id}: <- Success ${transitionStr}, final state: ${state}`);
}

/** @internalapi called by ui-router code */
Expand Down
7 changes: 6 additions & 1 deletion src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,15 @@ export interface UIInjector {
getNative<T>(token: any): T;
}

export interface UIRouterPlugin {
export interface UIRouterPlugin extends Disposable {
name: string;
}

export abstract class UIRouterPluginBase implements UIRouterPlugin {
abstract name: string;
dispose(router: UIRouter) { }
}

export interface Disposable {
dispose(router?: UIRouter);
}
5 changes: 5 additions & 0 deletions src/params/paramTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ export class ParamTypes {
this.types = inherit(map(this.defaultTypes, makeType), {});
}

/** @internalapi */
dispose() {
this.types = {};
}

type(name: string, definition?: ParamTypeDefinition, definitionFn?: () => ParamTypeDefinition) {
if (!isDefined(definition)) return this.types[name];
if (this.types.hasOwnProperty(name)) throw new Error(`A type named '${name}' has already been defined.`);
Expand Down
33 changes: 31 additions & 2 deletions src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import { ViewService } from "./view/view";
import { StateRegistry } from "./state/stateRegistry";
import { StateService } from "./state/stateService";
import { UIRouterGlobals, Globals } from "./globals";
import { UIRouterPlugin } from "./interface";
import { values } from "./common/common";
import { UIRouterPlugin, Disposable } from "./interface";
import { values, removeFrom } from "./common/common";

/** @hidden */
let _routerInstance = 0;

/**
* The master class used to instantiate an instance of UI-Router.
Expand All @@ -22,6 +25,9 @@ import { values } from "./common/common";
* Tell UI-Router to monitor the URL by calling `uiRouter.urlRouter.listen()` ([[UrlRouter.listen]])
*/
export class UIRouter {
/** @hidden */
$id: number = _routerInstance++;

viewService = new ViewService();

transitionService: TransitionService = new TransitionService(this);
Expand All @@ -38,10 +44,32 @@ export class UIRouter {

stateService = new StateService(this);

private _disposables: Disposable[] = [];

/** Registers an object to be notified when the router is disposed */
disposable(disposable: Disposable) {
this._disposables.push(disposable);
}

dispose() {
this._disposables.slice().forEach(d => {
try {
typeof d.dispose === 'function' && d.dispose(this);
removeFrom(this._disposables, d);
} catch (ignored) {}
});
}

constructor() {
this.viewService.rootContext(this.stateRegistry.root());
this.globals.$current = this.stateRegistry.root();
this.globals.current = this.globals.$current.self;

this.disposable(this.transitionService);
this.disposable(this.urlRouterProvider);
this.disposable(this.urlRouter);
this.disposable(this.stateRegistry);

}

private _plugins: { [key: string]: UIRouterPlugin } = {};
Expand Down Expand Up @@ -108,6 +136,7 @@ export class UIRouter {
plugin<T extends UIRouterPlugin>(plugin: any, options: any = {}): T {
let pluginInstance = new plugin(this, options);
if (!pluginInstance.name) throw new Error("Required property `name` missing on plugin: " + pluginInstance);
this._disposables.push(pluginInstance);
return this._plugins[pluginInstance.name] = pluginInstance;
}

Expand Down
8 changes: 7 additions & 1 deletion src/state/stateQueueManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import {UrlRouterProvider} from "../url/urlRouter";
import {RawParams} from "../params/interface";
import {StateRegistry, StateRegistryListener} from "./stateRegistry";
import { Param } from "../params/param";
import { Disposable } from "../interface";

export class StateQueueManager {
export class StateQueueManager implements Disposable {
queue: State[];
private $state: StateService;

Expand All @@ -23,6 +24,11 @@ export class StateQueueManager {
this.queue = [];
}

/** @internalapi */
dispose() {
this.queue = [];
}

register(config: StateDeclaration) {
let {states, queue, $state} = this;
// Wrap a new object around the state so we can store our private details easily.
Expand Down
27 changes: 17 additions & 10 deletions src/state/stateRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/** @coreapi @module state */ /** for typedoc */

import {State} from "./stateObject";
import {StateMatcher} from "./stateMatcher";
import {StateBuilder} from "./stateBuilder";
import {StateQueueManager} from "./stateQueueManager";
import {UrlMatcherFactory} from "../url/urlMatcherFactory";
import {StateDeclaration} from "./interface";
import {BuilderFunction} from "./stateBuilder";
import {StateOrName} from "./interface";
import {UrlRouterProvider} from "../url/urlRouter";
import {removeFrom} from "../common/common";
import { State } from "./stateObject";
import { StateMatcher } from "./stateMatcher";
import { StateBuilder } from "./stateBuilder";
import { StateQueueManager } from "./stateQueueManager";
import { UrlMatcherFactory } from "../url/urlMatcherFactory";
import { StateDeclaration } from "./interface";
import { BuilderFunction } from "./stateBuilder";
import { StateOrName } from "./interface";
import { UrlRouterProvider } from "../url/urlRouter";
import { removeFrom } from "../common/common";

/**
* The signature for the callback function provided to [[StateRegistry.onStateRegistryEvent]].
Expand Down Expand Up @@ -50,6 +50,13 @@ export class StateRegistry {
_root.navigable = null;
}

/** @internalapi */
dispose() {
this.stateQueue.dispose();
this.listeners = [];
this.get().forEach(state => this.get(state) && this.deregister(state));
}

/**
* Listen for a State Registry events
*
Expand Down
8 changes: 7 additions & 1 deletion src/state/stateService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @coreapi @module state */ /** */
import {extend, defaults, silentRejection, silenceUncaughtInPromise, removeFrom} from "../common/common";
import { extend, defaults, silentRejection, silenceUncaughtInPromise, removeFrom, noop } from "../common/common";
import {isDefined, isObject, isString} from "../common/predicates";
import {Queue} from "../common/queue";
import {services} from "../common/coreservices";
Expand Down Expand Up @@ -74,6 +74,12 @@ export class StateService {
bindFunctions(StateService.prototype, this, this, boundFns);
}

/** @internalapi */
dispose() {
this.defaultErrorHandler(noop);
this.invalidCallbacks = [];
}

/**
* Handler for when [[transitionTo]] is called with an invalid state.
*
Expand Down
4 changes: 1 addition & 3 deletions src/transition/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ import {Globals} from "../globals";
import {UIInjector} from "../interface";
import {RawParams} from "../params/interface";

/** @hidden */
let transitionCount = 0;
/** @hidden */
const stateSelf: (_state: State) => StateDeclaration = prop("self");

Expand Down Expand Up @@ -150,7 +148,7 @@ export class Transition implements IHookRegistry {

// current() is assumed to come from targetState.options, but provide a naive implementation otherwise.
this._options = extend({ current: val(this) }, targetState.options());
this.$id = transitionCount++;
this.$id = router.transitionService._transitionCount++;
let toPath = PathFactory.buildToPath(fromPath, targetState);
this._treeChanges = PathFactory.treeChanges(fromPath, toPath, this._options.reloadState);
this.createTransitionHookRegFns();
Expand Down
4 changes: 2 additions & 2 deletions src/transition/transitionHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class TransitionHook {
if (hook._deregistered) return;

let options = this.options;
trace.traceHookInvocation(this, options);
trace.traceHookInvocation(this, this.transition, options);

if (this.rejectIfSuperseded()) {
return Rejection.superseded(options.current()).toPromise();
Expand Down Expand Up @@ -114,7 +114,7 @@ export class TransitionHook {
return result.then(this.handleHookResult.bind(this));
}

trace.traceHookResult(result, this.options);
trace.traceHookResult(result, this.transition, this.options);

// Hook returned false
if (result === false) {
Expand Down
16 changes: 15 additions & 1 deletion src/transition/transitionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {registerLazyLoadHook} from "../hooks/lazyLoad";
import {TransitionHookType} from "./transitionHookType";
import {TransitionHook} from "./transitionHook";
import {isDefined} from "../common/predicates";
import { removeFrom, values } from "../common/common";
import { Disposable } from "../interface";

/**
* The default [[Transition]] options.
Expand Down Expand Up @@ -52,7 +54,9 @@ export let defaultTransOpts: TransitionOptions = {
*
* At bootstrap, [[UIRouter]] creates a single instance (singleton) of this class.
*/
export class TransitionService implements IHookRegistry {
export class TransitionService implements IHookRegistry, Disposable {
/** @hidden */
_transitionCount = 0;

/**
* Registers a [[TransitionHookFn]], called *while a transition is being constructed*.
Expand Down Expand Up @@ -128,6 +132,16 @@ export class TransitionService implements IHookRegistry {
this.registerTransitionHooks();
}

/** @internalapi */
dispose() {
delete this._router.globals.transition;

values(this._transitionHooks).forEach(hooksArray => hooksArray.forEach(hook => {
hook._deregistered = true;
removeFrom(hooksArray, hook);
}));
}

/**
* Creates a new [[Transition]] object
*
Expand Down
8 changes: 7 additions & 1 deletion src/url/urlMatcherFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {matcherConfig} from "./urlMatcherConfig";
import {Param} from "../params/param";
import {ParamTypes} from "../params/paramTypes";
import {ParamTypeDefinition} from "../params/interface";
import { Disposable } from "../interface";

/** @hidden */
function getDefaultConfig() {
Expand All @@ -22,7 +23,7 @@ function getDefaultConfig() {
* The factory is available to ng1 services as
* `$urlMatcherFactor` or ng1 providers as `$urlMatcherFactoryProvider`.
*/
export class UrlMatcherFactory {
export class UrlMatcherFactory implements Disposable {
paramTypes = new ParamTypes();

constructor() {
Expand Down Expand Up @@ -123,4 +124,9 @@ export class UrlMatcherFactory {
this.paramTypes._flushTypeQueue();
return this;
};

/** @internalapi */
dispose() {
this.paramTypes.dispose();
}
}
Loading

0 comments on commit 0690917

Please sign in to comment.