Skip to content

Commit f957501

Browse files
committed
#19 Allow for registering states using an injectable function (experimental atm).
1 parent 892823e commit f957501

39 files changed

+1686
-621
lines changed

build/angular-routing.js

+206-25
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,10 @@ var $RouteProvider = [
266266
$route: {
267267
name: expression.name,
268268
params: copy(expression.params),
269-
route: path
269+
route: path,
270+
remove: function () {
271+
delete routes[expression.name];
272+
}
270273
}
271274
};
272275
};
@@ -836,7 +839,7 @@ angular.module('dotjem.routing').provider('$route', $RouteProvider).value('$rout
836839
* Handlers for transitions can be specified in a number of ways, where the most simple handler is an injectable `function`.
837840
* <br/>
838841
* Here is a basic example:
839-
* <pre>
842+
* <pre dx-syntax class="brush: js">
840843
* angular.module('demo', ['dotjem.routing'])
841844
* .config(['$stateTransitionProvider', function(stp) {
842845
* stp
@@ -877,7 +880,7 @@ angular.module('dotjem.routing').provider('$route', $RouteProvider).value('$rout
877880
* When registering transitions like demonstrated in the example above, this will be maped to the "between" stage.
878881
* <br/>
879882
* To target specific stages of a transition use a transition object instead as in the example below:
880-
* <pre>
883+
* <pre dx-syntax class="brush: js">
881884
* angular.module('demo', ['dotjem.routing'])
882885
* .config(['$stateTransitionProvider', function(stp) {
883886
* stp
@@ -915,7 +918,7 @@ angular.module('dotjem.routing').provider('$route', $RouteProvider).value('$rout
915918
* <br/>
916919
* So if we just use `*` we target all existing states under `root`, and we can define a global handler that gets called on all transitions by using `*` both as destination and source.
917920
*
918-
* <pre>
921+
* <pre dx-syntax class="brush: js">
919922
* angular.module('demo', ['dotjem.routing'])
920923
* .config(['$stateTransitionProvider', function(stp) {
921924
* stp
@@ -927,7 +930,7 @@ angular.module('dotjem.routing').provider('$route', $RouteProvider).value('$rout
927930
*
928931
* We can also target all transitions to or from a specific state that way:
929932
*
930-
* <pre>
933+
* <pre dx-syntax class="brush: js">
931934
* angular.module('demo', ['dotjem.routing'])
932935
* .config(['$stateTransitionProvider', function(stp) {
933936
* stp
@@ -944,7 +947,7 @@ angular.module('dotjem.routing').provider('$route', $RouteProvider).value('$rout
944947
*
945948
* This was global handlers, but we might also wan't to target any state below a specific state:
946949
*
947-
* <pre>
950+
* <pre dx-syntax class="brush: js">
948951
* angular.module('demo', ['dotjem.routing'])
949952
* .config(['$stateTransitionProvider', function(stp) {
950953
* stp
@@ -963,7 +966,7 @@ angular.module('dotjem.routing').provider('$route', $RouteProvider).value('$rout
963966
*
964967
* In addition to using the `*` wildcart to target multiple states, it is also possible to use arrays for a more specific match.
965968
*
966-
* <pre>
969+
* <pre dx-syntax class="brush: js">
967970
* angular.module('demo', ['dotjem.routing'])
968971
* .config(['$stateTransitionProvider', function(stp) {
969972
* stp
@@ -984,7 +987,7 @@ angular.module('dotjem.routing').provider('$route', $RouteProvider).value('$rout
984987
*
985988
* Each of the states, wildcards can also be used:
986989
*
987-
* <pre>
990+
* <pre dx-syntax class="brush: js">
988991
* angular.module('demo', ['dotjem.routing'])
989992
* .config(['$stateTransitionProvider', function(stp) {
990993
* stp
@@ -1190,11 +1193,10 @@ function $StateTransitionProvider() {
11901193
function ($q, $inject) {
11911194
var $transition = {
11921195
root: root,
1193-
find: find
1196+
find: find,
1197+
to: noop
11941198
};
11951199

1196-
return $transition;
1197-
11981200
function find(from, to) {
11991201
var transitions = findTransitions(toName(from)), handlers = extractHandlers(transitions, toName(to));
12001202

@@ -1291,6 +1293,12 @@ function $StateTransitionProvider() {
12911293
} while(index++ < names.length);
12921294
return transitions;
12931295
}
1296+
1297+
//var current = $q.when(null);
1298+
//function to(args: { state; params; updateroute?; }) {
1299+
// current.then(function () { });
1300+
//}
1301+
return $transition;
12941302
}];
12951303
}
12961304
angular.module('dotjem.routing').provider('$stateTransition', $StateTransitionProvider);
@@ -1305,7 +1313,7 @@ angular.module('dotjem.routing').provider('$stateTransition', $StateTransitionPr
13051313
* <br/>
13061314
* Here is a very basic example of configuring states.
13071315
*
1308-
* <pre>
1316+
* <pre dx-syntax class="brush: js">
13091317
* angular.module('demo', ['dotjem.routing']).
13101318
* config(['$stateProvider', function($stateProvider) {
13111319
* $stateProvider
@@ -1435,7 +1443,7 @@ var $StateProvider = [
14351443
*
14361444
* The following registrations would result in the ilustated hierachy.
14371445
*
1438-
* <pre>
1446+
* <pre dx-syntax class="brush: js">
14391447
* .state('home', {})
14401448
* .state('home.recents', {})
14411449
* .state('home.all', {})
@@ -1462,19 +1470,89 @@ var $StateProvider = [
14621470
* @returns {Object} self
14631471
*
14641472
* @description
1465-
* Adds a new route definition to the `$route` service.
1473+
* Adds a new state definition to the `$state` service.
14661474
*/
1467-
this.state = function (fullname, state) {
1475+
/**
1476+
* @ngdoc method
1477+
* @name dotjem.routing.$stateProvider#state
1478+
* @methodOf dotjem.routing.$stateProvider
1479+
*
1480+
* @param {function} Registration function, this is an injectable function and can be used to load state configurations
1481+
* from the backend, e.g. using the `$http` service.
1482+
*
1483+
* The function should at least depend on `$register` which is used in place of the {@link dotjem.routing.$stateProvider#state state} function.
1484+
*
1485+
* <pre dx-syntax class="brush: js">
1486+
* .state(['$register', '$http', function($register, $http) {
1487+
* return $http.get('/stateConfig').then(function (result) {
1488+
* // result is delivered as:
1489+
* // {
1490+
* // 'state1name': { ...params },
1491+
* // 'state2name': { ...params }
1492+
* // }
1493+
* // in this example.
1494+
* angular.forEach(result.data, function (state, name) {
1495+
* $register(name, state);
1496+
* });
1497+
* });
1498+
* }])
1499+
* </pre>
1500+
*
1501+
* Note: The function should return a promise that is resolved when registration is done, so that the state service knows when it can resume normal operation.
1502+
*
1503+
* @returns {Object} self
1504+
*/
1505+
this.state = function (nameOrFunc, state) {
1506+
if (!isInjectable(nameOrFunc)) {
1507+
StateRules.validateName(nameOrFunc);
1508+
1509+
initializers.push(function () {
1510+
internalRegisterState(nameOrFunc, state);
1511+
return null;
1512+
});
1513+
} else {
1514+
initializers.push(function () {
1515+
return nameOrFunc;
1516+
});
1517+
}
1518+
return this;
1519+
};
1520+
1521+
function registerState(fullname, state) {
14681522
StateRules.validateName(fullname);
14691523

1524+
internalRegisterState(fullname, state);
1525+
}
1526+
1527+
function internalRegisterState(fullname, state) {
14701528
var parent = browser.lookup(fullname, 1);
14711529
parent.add(factory.createState(fullname, state, parent));
1472-
return this;
1473-
};
1530+
}
1531+
1532+
var initializers = [];
14741533

14751534
this.$get = [
1476-
'$rootScope', '$q', '$inject', '$route', '$view', '$stateTransition', '$location', '$scroll', '$resolve',
1477-
function ($rootScope, $q, $inject, $route, $view, $transition, $location, $scroll, $resolve) {
1535+
'$rootScope', '$q', '$inject', '$route', '$view', '$stateTransition', '$location', '$scroll', '$resolve', '$exceptionHandler',
1536+
function ($rootScope, $q, $inject, $route, $view, $transition, $location, $scroll, $resolve, $exceptionHandler) {
1537+
function init(promise) {
1538+
root.clear($routeProvider);
1539+
1540+
forEach(initializers, function (init) {
1541+
try {
1542+
var injectable = init();
1543+
if (injectable !== null) {
1544+
promise = promise.then(function () {
1545+
return $inject.invoke(injectable, injectable, { $register: registerState });
1546+
});
1547+
}
1548+
} catch (error) {
1549+
$exceptionHandler(error);
1550+
}
1551+
});
1552+
return promise;
1553+
}
1554+
var initPromise = init($q.when(0));
1555+
14781556
/**
14791557
* @ngdoc object
14801558
* @name dotjem.routing.$state
@@ -1716,24 +1794,41 @@ var $StateProvider = [
17161794
*
17171795
* @returns {boolean} true if the stats mathces, otherwise false.
17181796
*/
1797+
/**
1798+
* @ngdoc method
1799+
* @name dotjem.routing.$state#reinitialize
1800+
* @methodOf dotjem.routing.$state
1801+
*
1802+
* @description
1803+
* Clears all states and associated routes and reinitializes the state service.
1804+
*/
17191805
var urlbuilder = new StateUrlBuilder($route);
17201806

17211807
var forceReload = null, current = root, $state = {
17221808
// NOTE: root should not be used in general, it is exposed for testing purposes.
17231809
root: root,
17241810
current: extend(root.self, { $params: buildParams() }),
17251811
params: buildParams(),
1812+
reinitialize: function () {
1813+
return initPromise = init(initPromise);
1814+
},
17261815
goto: function (state, params) {
1727-
goto({
1728-
state: state,
1729-
params: buildParams(params),
1730-
updateroute: true
1816+
return initPromise.then(function () {
1817+
goto({
1818+
state: state,
1819+
params: buildParams(params),
1820+
updateroute: true
1821+
});
17311822
});
17321823
},
17331824
lookup: function (path) {
17341825
return browser.resolve(current, path, true);
17351826
},
1736-
reload: reload,
1827+
reload: function (state) {
1828+
return initPromise.then(function () {
1829+
reload(state);
1830+
});
1831+
},
17371832
url: function (arg1, arg2, arg3) {
17381833
var state = current;
17391834
if (arguments.length === 0) {
@@ -1779,6 +1874,7 @@ var $StateProvider = [
17791874
goto({ state: root, params: buildParams() });
17801875
}
17811876
});
1877+
17821878
$rootScope.$on(EVENTS.ROUTE_UPDATE, function () {
17831879
var route = $route.current, params = buildParams(route.params, route.pathParams, route.searchParams);
17841880

@@ -1811,15 +1907,22 @@ var $StateProvider = [
18111907
var context = new Context($state, function () {
18121908
}, root).complete();
18131909
var running = context;
1910+
var comparer = new StateComparer();
18141911

18151912
function goto(args) {
18161913
var ctx, scrollTo, useUpdate = false;
18171914

1818-
//$transition.create(args.state, args.params, up)
18191915
if (!running.ended) {
18201916
running.abort();
18211917
}
18221918

1919+
//var next = browser.resolve(current, toName(args.state), false);
1920+
//var path = comparer.path(current, next, $state.params, args.params);
1921+
//$transition.to(args, function (state) {
1922+
// $state.params = state.$params;
1923+
// $state.current = state;
1924+
//});
1925+
//$transition.create(args.state, args.params, up)
18231926
ctx = running = context.next(function (ctx) {
18241927
context = ctx;
18251928
});
@@ -2891,6 +2994,18 @@ var State = (function () {
28912994
return this.fullname === state || this.fullname === rootName + '.' + state;
28922995
};
28932996

2997+
State.prototype.clear = function (route) {
2998+
forEach(this._children, function (state) {
2999+
state.clear(route);
3000+
});
3001+
3002+
if (this._route) {
3003+
this._route.remove();
3004+
}
3005+
3006+
this._children = {};
3007+
};
3008+
28943009
State.prototype.isActive = function (state) {
28953010
if (this.is(state)) {
28963011
return true;
@@ -3072,6 +3187,72 @@ var StateComparer = (function () {
30723187
paramChanges: paramChanges
30733188
};
30743189
};
3190+
3191+
StateComparer.prototype.isSameState = function (from, to) {
3192+
if (from === to) {
3193+
return true;
3194+
}
3195+
3196+
//Note: If one of them is undefined, note that if both are undefined the above if would have returned.
3197+
if (isUndefined(from) || isUndefined(to)) {
3198+
return false;
3199+
}
3200+
3201+
return to.name === from.name;
3202+
};
3203+
3204+
StateComparer.prototype.isEquals = function (from, to) {
3205+
return this.isSameState(from, to) && equals(to.params, from.params);
3206+
};
3207+
3208+
StateComparer.prototype.path = function (from, to, fromParams, toParams) {
3209+
var fromArray = this.toArray(from, fromParams, false), toArray = this.toArray(to, toParams, true), count = Math.max(fromArray.length, toArray.length);
3210+
3211+
var unchanged = [];
3212+
var deactivate = [];
3213+
var activate = [];
3214+
var change = {};
3215+
3216+
for (var i = 0; i < count; i++) {
3217+
var f = fromArray[i], t = toArray[i];
3218+
if (this.isEquals(f, t)) {
3219+
unchanged.push(f);
3220+
} else if (this.isSameState(f, t)) {
3221+
deactivate.push(f);
3222+
activate.push(t);
3223+
} else {
3224+
deactivate = deactivate.concat(fromArray.slice(i, fromArray.length));
3225+
deactivate.reverse();
3226+
3227+
activate = activate.concat(toArray.slice(i, toArray.length));
3228+
break;
3229+
}
3230+
}
3231+
3232+
change.changed = deactivate.concat(activate);
3233+
change.unchanged = unchanged;
3234+
3235+
return change;
3236+
};
3237+
3238+
StateComparer.prototype.toArray = function (state, params, activate) {
3239+
var states = [], current = state;
3240+
do {
3241+
states.push({ state: current, name: current.fullname, params: this.extractParams(params, current), activate: activate });
3242+
} while(current = current.parent);
3243+
states.reverse();
3244+
return states;
3245+
};
3246+
3247+
StateComparer.prototype.extractParams = function (params, current) {
3248+
var paramsObj = {};
3249+
if (current.route) {
3250+
forEach(current.route.params, function (param, name) {
3251+
paramsObj[name] = params[name];
3252+
});
3253+
}
3254+
return paramsObj;
3255+
};
30753256
return StateComparer;
30763257
})();
30773258

build/angular-routing.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/src/route.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ var $RouteProvider = [
118118
$route: {
119119
name: expression.name,
120120
params: copy(expression.params),
121-
route: path
121+
route: path,
122+
remove: function () {
123+
delete routes[expression.name];
124+
}
122125
}
123126
};
124127
};

0 commit comments

Comments
 (0)