Skip to content

Commit f07ab2d

Browse files
committed
Merge pull request angular-ui#399 from angular-ui/decorators
Decorator API
2 parents e8ea509 + 5880293 commit f07ab2d

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)
@@ -501,4 +502,60 @@ describe('state', function () {
501502
expect(templateParams).toEqual({ item: "foo" });
502503
}));
503504
});
505+
506+
describe('provider decorators', function () {
507+
508+
it('should return built-in decorators', function () {
509+
expect(stateProvider.decorator('parent')({ parent: A }).self.name).toBe("A");
510+
});
511+
512+
it('should allow built-in decorators to be overridden', inject(function ($state, $q) {
513+
stateProvider.decorator('data', function(state) {
514+
return angular.extend(state.data || {}, { foo: "bar" });
515+
});
516+
stateProvider.state('AA', { parent: A, data: { baz: "true" } });
517+
518+
$state.transitionTo('AA');
519+
$q.flush();
520+
expect($state.current.data).toEqual({ baz: 'true', foo: 'bar' });
521+
}));
522+
523+
it('should allow new decorators to be added', inject(function ($state, $q) {
524+
stateProvider.decorator('custom', function(state) {
525+
return function() { return "Custom functionality for state '" + state + "'" };
526+
});
527+
stateProvider.state('decoratorTest', {});
528+
529+
$state.transitionTo('decoratorTest');
530+
$q.flush();
531+
expect($state.$current.custom()).toBe("Custom functionality for state 'decoratorTest'");
532+
}));
533+
534+
it('should allow built-in decorators to be extended', inject(function ($state, $q, $httpBackend) {
535+
stateProvider.decorator('views', function(state, parent) {
536+
var result = {};
537+
538+
angular.forEach(parent(state), function(config, name) {
539+
result[name] = angular.extend(config, { templateProvider: function() {
540+
return "Template for " + name;
541+
}});
542+
});
543+
return result;
544+
});
545+
546+
stateProvider.state('viewTest', {
547+
views: {
548+
viewA: {},
549+
viewB: {}
550+
}
551+
});
552+
553+
$state.transitionTo('viewTest');
554+
$q.flush();
555+
556+
expect($state.$current.views['viewA@'].templateProvider()).toBe('Template for viewA@');
557+
expect($state.$current.views['viewB@'].templateProvider()).toBe('Template for viewB@');
558+
}));
559+
560+
});
504561
});

0 commit comments

Comments
 (0)