ui-router-core
Version:
UI-Router Core: Framework agnostic, State-based routing for JavaScript Single Page Apps
131 lines • 5.74 kB
JavaScript
/**
* @coreapi
* @module resolve
*/ /** for typedoc */
import { extend, identity } from "../common/common";
import { services } from "../common/coreservices";
import { trace } from "../common/trace";
import { stringify } from "../common/strings";
import { isFunction, isObject } from "../common/predicates";
// TODO: explicitly make this user configurable
export var defaultResolvePolicy = {
when: "LAZY",
async: "WAIT"
};
/**
* The basic building block for the resolve system.
*
* Resolvables encapsulate a state's resolve's resolveFn, the resolveFn's declared dependencies, the wrapped (.promise),
* and the unwrapped-when-complete (.data) result of the resolveFn.
*
* Resolvable.get() either retrieves the Resolvable's existing promise, or else invokes resolve() (which invokes the
* resolveFn) and returns the resulting promise.
*
* Resolvable.get() and Resolvable.resolve() both execute within a context path, which is passed as the first
* parameter to those fns.
*/
var Resolvable = (function () {
function Resolvable(arg1, resolveFn, deps, policy, data) {
this.resolved = false;
this.promise = undefined;
if (arg1 instanceof Resolvable) {
extend(this, arg1);
}
else if (isFunction(resolveFn)) {
if (arg1 == null || arg1 == undefined)
throw new Error("new Resolvable(): token argument is required");
if (!isFunction(resolveFn))
throw new Error("new Resolvable(): resolveFn argument must be a function");
this.token = arg1;
this.policy = policy;
this.resolveFn = resolveFn;
this.deps = deps || [];
this.data = data;
this.resolved = data !== undefined;
this.promise = this.resolved ? services.$q.when(this.data) : undefined;
}
else if (isObject(arg1) && arg1.token && isFunction(arg1.resolveFn)) {
var literal = arg1;
return new Resolvable(literal.token, literal.resolveFn, literal.deps, literal.policy, literal.data);
}
}
Resolvable.prototype.getPolicy = function (state) {
var thisPolicy = this.policy || {};
var statePolicy = state && state.resolvePolicy || {};
return {
when: thisPolicy.when || statePolicy.when || defaultResolvePolicy.when,
async: thisPolicy.async || statePolicy.async || defaultResolvePolicy.async,
};
};
/**
* Asynchronously resolve this Resolvable's data
*
* Given a ResolveContext that this Resolvable is found in:
* Wait for this Resolvable's dependencies, then invoke this Resolvable's function
* and update the Resolvable's state
*/
Resolvable.prototype.resolve = function (resolveContext, trans) {
var _this = this;
var $q = services.$q;
// Gets all dependencies from ResolveContext and wait for them to be resolved
var getResolvableDependencies = function () {
return $q.all(resolveContext.getDependencies(_this).map(function (resolvable) {
return resolvable.get(resolveContext, trans);
}));
};
// Invokes the resolve function passing the resolved dependencies as arguments
var invokeResolveFn = function (resolvedDeps) {
return _this.resolveFn.apply(null, resolvedDeps);
};
/**
* For RXWAIT policy:
*
* Given an observable returned from a resolve function:
* - enables .cache() mode (this allows multicast subscribers)
* - then calls toPromise() (this triggers subscribe() and thus fetches)
* - Waits for the promise, then return the cached observable (not the first emitted value).
*/
var waitForRx = function (observable$) {
var cached = observable$.cache(1);
return cached.take(1).toPromise().then(function () { return cached; });
};
// If the resolve policy is RXWAIT, wait for the observable to emit something. otherwise pass through.
var node = resolveContext.findNode(this);
var state = node && node.state;
var maybeWaitForRx = this.getPolicy(state).async === "RXWAIT" ? waitForRx : identity;
// After the final value has been resolved, update the state of the Resolvable
var applyResolvedValue = function (resolvedValue) {
_this.data = resolvedValue;
_this.resolved = true;
trace.traceResolvableResolved(_this, trans);
return _this.data;
};
// Sets the promise property first, then getsResolvableDependencies in the context of the promise chain. Always waits one tick.
return this.promise = $q.when()
.then(getResolvableDependencies)
.then(invokeResolveFn)
.then(maybeWaitForRx)
.then(applyResolvedValue);
};
/**
* Gets a promise for this Resolvable's data.
*
* Fetches the data and returns a promise.
* Returns the existing promise if it has already been fetched once.
*/
Resolvable.prototype.get = function (resolveContext, trans) {
return this.promise || this.resolve(resolveContext, trans);
};
Resolvable.prototype.toString = function () {
return "Resolvable(token: " + stringify(this.token) + ", requires: [" + this.deps.map(stringify) + "])";
};
Resolvable.prototype.clone = function () {
return new Resolvable(this);
};
return Resolvable;
}());
export { Resolvable };
Resolvable.fromData = function (token, data) {
return new Resolvable(token, function () { return data; }, null, null, data);
};
//# sourceMappingURL=resolvable.js.map