Skip to content

Commit d026ee4

Browse files
committed
First pass at $stateProvider decorator API.
1 parent 4fc388b commit d026ee4

File tree

2 files changed

+81
-3
lines changed

2 files changed

+81
-3
lines changed

src/state.js

+23-2
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
102102
var includes = state.parent ? extend({}, state.parent.includes) : {};
103103
includes[state.name] = true;
104104
return includes;
105-
}
105+
},
106+
107+
$delegates: {}
106108
};
107109

108110
function isRelative(stateName) {
@@ -155,7 +157,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
155157
if (states[name]) throw new Error("State '" + name + "'' is already defined");
156158

157159
for (var key in stateBuilder) {
158-
state[key] = stateBuilder[key](state);
160+
if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
159161
}
160162
states[name] = state;
161163

@@ -181,6 +183,25 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
181183
root.navigable = null;
182184

183185

186+
// .decorator()
187+
// .decorator(name)
188+
// .decorator(name, function)
189+
this.decorator = decorator;
190+
function decorator(name, func) {
191+
/*jshint validthis: true */
192+
if (isString(name) && !isDefined(func)) {
193+
return stateBuilder[name];
194+
}
195+
if (!isFunction(func) || !isString(name)) {
196+
return this;
197+
}
198+
if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
199+
stateBuilder.$delegates[name] = stateBuilder[name];
200+
}
201+
stateBuilder[name] = func;
202+
return this;
203+
}
204+
184205
// .state(state)
185206
// .state(name, state)
186207
this.state = state;

test/stateSpec.js

+58-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
describe('state', function () {
22

3-
var locationProvider, templateParams;
3+
var stateProvider, locationProvider, templateParams;
44

55
beforeEach(module('ui.router', function($locationProvider) {
66
locationProvider = $locationProvider;
@@ -33,6 +33,7 @@ describe('state', function () {
3333
state.onEnter = callbackLogger('onEnter');
3434
state.onExit = callbackLogger('onExit');
3535
});
36+
stateProvider = $stateProvider;
3637

3738
$stateProvider
3839
.state('A', A)
@@ -478,4 +479,60 @@ describe('state', function () {
478479
expect(templateParams).toEqual({ item: "foo" });
479480
}));
480481
});
482+
483+
describe('provider decorators', function () {
484+
485+
it('should return built-in decorators', function () {
486+
expect(stateProvider.decorator('parent')({ parent: A }).self.name).toBe("A");
487+
});
488+
489+
it('should allow built-in decorators to be overridden', inject(function ($state, $q) {
490+
stateProvider.decorator('data', function(state) {
491+
return angular.extend(state.data || {}, { foo: "bar" });
492+
});
493+
stateProvider.state('AA', { parent: A, data: { baz: "true" } });
494+
495+
$state.transitionTo('AA');
496+
$q.flush();
497+
expect($state.current.data).toEqual({ baz: 'true', foo: 'bar' });
498+
}));
499+
500+
it('should allow new decorators to be added', inject(function ($state, $q) {
501+
stateProvider.decorator('custom', function(state) {
502+
return function() { return "Custom functionality for state '" + state + "'" };
503+
});
504+
stateProvider.state('decoratorTest', {});
505+
506+
$state.transitionTo('decoratorTest');
507+
$q.flush();
508+
expect($state.$current.custom()).toBe("Custom functionality for state 'decoratorTest'");
509+
}));
510+
511+
it('should allow built-in decorators to be extended', inject(function ($state, $q, $httpBackend) {
512+
stateProvider.decorator('views', function(state, parent) {
513+
var result = {};
514+
515+
angular.forEach(parent(state), function(config, name) {
516+
result[name] = angular.extend(config, { templateFactory: function() {
517+
return "Template for " + name;
518+
}});
519+
});
520+
return result;
521+
});
522+
523+
stateProvider.state('viewTest', {
524+
views: {
525+
viewA: {},
526+
viewB: {}
527+
}
528+
});
529+
530+
$state.transitionTo('viewTest');
531+
$q.flush();
532+
533+
expect($state.$current.views['viewA@'].templateFactory()).toBe('Template for viewA@');
534+
expect($state.$current.views['viewB@'].templateFactory()).toBe('Template for viewB@');
535+
}));
536+
537+
});
481538
});

0 commit comments

Comments
 (0)