Skip to content

Instantly share code, notes, and snippets.

@alextkachman
Last active August 29, 2015 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alextkachman/bda667b09c0c4b7f9ee8 to your computer and use it in GitHub Desktop.
Save alextkachman/bda667b09c0c4b7f9ee8 to your computer and use it in GitHub Desktop.
Patch for injector to resolve promises
(function(angular){
"use strict";
/**
* Patched $injector.invoke
*
* It allows in many situations to avoid promise related boilerplate like
*
* factory('$loggedUser',['$login','$loadedConfig',function($login,$loadedConfig){
* return $login.then(function($login){
* return $loadedConfig.then(function($loadedConfig){
* return createLoggedUser($login,$loadedConfig)
* })
* })
* ])
*
* or alternatively
*
* factory('$loggedUser',['$login','$loadedConfig','$q',function($login,$loadedConfig,$q){
* return $q.all({$login:$login,$loadedConfig:$loadedConfig{).then(function(resolved){
* return createLoggedUser(resolved.$login,resolved.$loadedConfig)
* })
* ])
*
* and simply use instead
*
* factory('$loggedUser',['@$login','@$loadedConfig',function($login,$loadedConfig){
* return createLoggedUser($login,$loadedConfig)
* ])
*
* If any of services required for annotated function is prefixed with @ char
* - each such service treated as promise
* - the function automatically converted in to the one returning promise
* - the function will be executed after all such promise-services resolved
* - return value of original function will become resolved value of final function
*/
angular.module('InjectorEx',[])
.run(['$injector','$q',function($injector,$q) {
function doPatch(fn,fnToCall) {
var i, ii,
patched
for (i = 0, ii = fn.length - 1; i != ii; ++i) {
if (fn[i].charAt(0) == '@') {
//noinspection JSUnusedAssignment
(patched || (patched = [])).push(i)
}
}
if (patched) {
fn = fn.slice()
for (i = 0, ii = patched.length; i != ii; ++i) {
fn[patched[i]] = fn[patched[i]].substring(1)
}
fn [fn.length - 1] = function () {
var resolve = [],
origThis = this,
i,
newArgs
for (i = 0; i != patched.length; ++i) {
resolve.push(arguments[patched[i]])
}
newArgs = Array.prototype.slice.call(arguments)
return $q.all(resolve).then(function (resolved) {
for (var i = 0, ii = patched.length; i != ii; ++i) {
newArgs[patched[i]] = resolved[i]
}
return fnToCall.apply(origThis, newArgs)
})
}
}
return fn
}
function patch(fn) {
if (!angular.isArray(fn)) {
return fn
}
var fnToCall = fn[fn.length-1]
return fnToCall.$$patch || (fnToCall.$$patch = doPatch(fn,fnToCall))
}
$injector.invoke = (function(){
var orig$invoke = $injector.invoke
return function invoke(fn, self, locals){
return orig$invoke.call(this, patch(fn), self, locals)
}
})()
/**
* We patch instantiate exclusively in order to use patched invoke
*/
$injector.instantiate = function instantiate(Type, locals) {
var Constructor = function() {},
instance,
returnedValue;
Constructor.prototype = (angular.isArray(Type) ? Type[Type.length - 1] : Type).prototype;
instance = new Constructor();
/*
* This line is suppose to be the only material diff with original function
*/
returnedValue = $injector.invoke(Type, instance, locals);
return angular.isObject(returnedValue) || angular.isFunction(returnedValue) ? returnedValue : instance;
}
}])
})(angular);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment