Skip to content

Instantly share code, notes, and snippets.

@alexkunin
Created August 30, 2013 22:02
Show Gist options
  • Save alexkunin/6394739 to your computer and use it in GitHub Desktop.
Save alexkunin/6394739 to your computer and use it in GitHub Desktop.
Solution to https://github.com/angular-ui/ui-router/pull/73, sugar-enabled alternative to https://gist.github.com/MattWalker/6106393. Includes "dependency reordering' (sort of) and circular reference checks.
angular.module("yourApp").provider("$stateProviderWrapper", function ($stateProvider, $injector) {
var stack = {
"":{}
};
return {
$get:function () {
},
state:function (name, definition) {
// Apply same argument manipulations as $stateProvider.state does
if (angular.isObject(name)) {
definition = name;
} else {
definition.name = name;
}
// Look for all things to resolve
var resolves = {};
if (definition.resolve) {
resolves[""] = definition.resolve;
}
if (definition.views) {
angular.forEach(definition.views, function (view, name) {
if (view && view.resolve) {
resolves[name] = view.resolve;
}
});
}
// Make sure everything is invokable
angular.forEach(resolves, function (resolve) {
angular.forEach(resolve, function (value, name) {
if (angular.isString(value)) {
resolve[name] = function () {
return $injector.get(value);
};
}
});
});
// Enhance everything
angular.forEach(resolves, function (resolve, viewName) {
// Create unique map (dependency -> deferred), inherit from parent state
var deferred = stack[name + "/" + viewName] = stack[name + "/" + viewName] || (function () {
var f = function () {},
parentName = "";
if (!angular.isDefined(definition.parent)) {
var compositeName = /^(.+)\.[^.]+$/.exec(name);
if (compositeName != null) {
parentName = compositeName[1];
}
} else if (definition.parent != null) {
parentName = definition.parent.name || definition.parent;
}
f.prototype = stack[parentName + "/"] || stack[""] || {};
return new f();
}());
function depends(a, b, seen) {
if (!resolve[a]) {
throw new Error("Unresolved dependency \"" + a + "\"");
}
if (!seen) {
seen = [];
}
if (seen.indexOf(a) != -1) {
throw new Error("Circular dependency: " + seen.concat([ a ]).join(" -> "));
}
return $injector.annotate(resolve[a]).some(function (name) {
if ($injector.has(name)) {
return false;
}
return resolve[name] && (name == b || depends(name, b, seen.concat([ a ])));
});
}
Object.keys(resolve).sort(function (a, b) {
return depends(a, b) ? 1 : depends(b, a) ? -1 :0;
}).forEach(function (name) {
var resolver = resolve[name];
resolve[name] = function ($q, $injector, $stateParams) {
if (!deferred.hasOwnProperty(name)) {
deferred[name] = $q.defer();
}
var input = {
$stateParams:$stateParams
};
$injector.annotate(resolver).forEach(function (name) {
if (!$injector.has(name)) {
if (!deferred[name]) {
if (!resolve[name]) {
// Let $injector.invoke deal with this property
return;
} else {
deferred[name] = $q.defer();
}
}
input[name] = deferred[name].promise;
}
});
return $q.all(input).then(function (output) {
return $q.when($injector.invoke(resolver, definition, output)).then(function (result) {
deferred[name].resolve(result);
return result;
});
});
};
resolve[name].$inject = [ "$q", "$injector", "$stateParams" ];
});
});
$stateProvider.state(name, definition);
return this;
}
};
});
angular.module("yourApp").config(function ($stateProviderWrapperProvider) {
$stateProviderWrapperProvider
.state("parent", {
resolve:{
a:function () {
return 1;
},
b:function (a) {
return a + 1;
}
}
})
.state("parent.child", {
resolve:{
c:function (a, b) {
return a * b;
}
}
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment