Skip to content

Commit 409f2e1

Browse files
cherry-pick 0f6aea6 from 1.0 to legacy
feat(uiView): Expose the resolved data for a state as $scope.$resolve feat(uiView): Route to components using templates closes #2175 closes #2547
1 parent eb7c3ab commit 409f2e1

File tree

3 files changed

+109
-2
lines changed

3 files changed

+109
-2
lines changed

src/state.js

+2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
8989

9090
forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
9191
if (name.indexOf('@') < 0) name += '@' + state.parent.name;
92+
view.resolveAs = view.resolveAs || state.resolveAs || '$resolve';
9293
views[name] = view;
9394
});
9495
return views;
@@ -1421,6 +1422,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
14211422
// Provide access to the state itself for internal use
14221423
result.$$state = state;
14231424
result.$$controllerAs = view.controllerAs;
1425+
result.$$resolveAs = view.resolveAs;
14241426
dst[name] = result;
14251427
}));
14261428
});

src/viewDirective.js

+23
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,25 @@
110110
* <ui-view autoscroll='false'/>
111111
* <ui-view autoscroll='scopeVariable'/>
112112
* </pre>
113+
*
114+
* Resolve data:
115+
*
116+
* The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this
117+
* can be customized using [[ViewDeclaration.resolveAs]]). This can be then accessed from the template.
118+
*
119+
* Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the
120+
* controller is instantiated. The `$onInit()` hook can be used to perform initialization code which
121+
* depends on `$resolve` data.
122+
*
123+
* Example usage of $resolve in a view template
124+
* <pre>
125+
* $stateProvider.state('home', {
126+
* template: '<my-component user="$resolve.user"></my-component>',
127+
* resolve: {
128+
* user: function(UserService) { return UserService.fetchUser(); }
129+
* }
130+
* });
131+
* </pre>
113132
*/
114133
$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate', '$q'];
115134
function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate, $q) {
@@ -300,6 +319,9 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
300319
extend($uiViewData, { state: locals.$$state });
301320
$element.html(locals.$template ? locals.$template : initial);
302321

322+
var resolveData = angular.extend({}, locals);
323+
scope[locals.$$resolveAs] = resolveData;
324+
303325
var link = $compile($element.contents());
304326

305327
if (locals.$$controller) {
@@ -308,6 +330,7 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
308330
var controller = $controller(locals.$$controller, locals);
309331
if (locals.$$controllerAs) {
310332
scope[locals.$$controllerAs] = controller;
333+
scope[locals.$$controllerAs][locals.$$resolveAs] = resolveData;
311334
}
312335
$element.data('$ngControllerController', controller);
313336
$element.children().data('$ngControllerController', controller);

test/viewDirectiveSpec.js

+84-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function animateFlush($animate) {
99
describe('uiView', function () {
1010
'use strict';
1111

12-
var scope, $compile, elem, log;
12+
var $stateProvider, scope, $compile, elem, log;
1313

1414
beforeEach(function() {
1515
var depends = ['ui.router'];
@@ -120,7 +120,8 @@ describe('uiView', function () {
120120
}
121121
};
122122

123-
beforeEach(module(function ($stateProvider) {
123+
beforeEach(module(function (_$stateProvider_) {
124+
$stateProvider = _$stateProvider_;
124125
$stateProvider
125126
.state('a', aState)
126127
.state('b', bState)
@@ -338,6 +339,87 @@ describe('uiView', function () {
338339
expect(elem.text()).toBe('mState');
339340
}));
340341

342+
describe('(resolved data)', function() {
343+
var _scope;
344+
function controller($scope) { _scope = $scope; }
345+
346+
var _state = {
347+
name: 'resolve',
348+
resolve: {
349+
user: function($timeout) {
350+
return $timeout(function() { return "joeschmoe"; }, 100);
351+
}
352+
}
353+
};
354+
355+
it('should provide the resolved data on the $scope', inject(function ($state, $q, $timeout) {
356+
var state = angular.extend({}, _state, { template: '{{$resolve.user}}', controller: controller });
357+
$stateProvider.state(state);
358+
elem.append($compile('<div><ui-view></ui-view></div>')(scope));
359+
360+
$state.transitionTo('resolve'); $q.flush(); $timeout.flush();
361+
expect(elem.text()).toBe('joeschmoe');
362+
expect(_scope.$resolve).toBeDefined();
363+
expect(_scope.$resolve.user).toBe('joeschmoe')
364+
}));
365+
366+
it('should put the resolved data on the resolveAs variable', inject(function ($state, $q, $timeout) {
367+
var state = angular.extend({}, _state, { template: '{{$$$resolve.user}}', resolveAs: '$$$resolve', controller: controller });
368+
$stateProvider.state(state);
369+
elem.append($compile('<div><ui-view></ui-view></div>')(scope));
370+
371+
$state.transitionTo('resolve'); $q.flush(); $timeout.flush();
372+
expect(elem.text()).toBe('joeschmoe');
373+
expect(_scope.$$$resolve).toBeDefined();
374+
expect(_scope.$$$resolve.user).toBe('joeschmoe')
375+
}));
376+
377+
it('should put the resolved data on the controllerAs', inject(function ($state, $q, $timeout) {
378+
var state = angular.extend({}, _state, { template: '{{$ctrl.$resolve.user}}', controllerAs: '$ctrl', controller: controller });
379+
$stateProvider.state(state);
380+
elem.append($compile('<div><ui-view></ui-view></div>')(scope));
381+
382+
$state.transitionTo('resolve'); $q.flush(); $timeout.flush();
383+
expect(elem.text()).toBe('joeschmoe');
384+
expect(_scope.$resolve).toBeDefined();
385+
expect(_scope.$ctrl).toBeDefined();
386+
expect(_scope.$ctrl.$resolve).toBeDefined();
387+
expect(_scope.$ctrl.$resolve.user).toBe('joeschmoe');
388+
}));
389+
390+
it('should use the view-level resolveAs over the state-level resolveAs', inject(function ($state, $q, $timeout) {
391+
var views = {
392+
"": {
393+
controller: controller,
394+
template: '{{$$$resolve.user}}',
395+
resolveAs: '$$$resolve'
396+
}
397+
};
398+
var state = angular.extend({}, _state, { resolveAs: 'foo', views: views });
399+
$stateProvider.state(state);
400+
elem.append($compile('<div><ui-view></ui-view></div>')(scope));
401+
402+
$state.transitionTo('resolve'); $q.flush(); $timeout.flush();
403+
expect(elem.text()).toBe('joeschmoe');
404+
expect(_scope.$$$resolve).toBeDefined();
405+
expect(_scope.$$$resolve.user).toBe('joeschmoe');
406+
}));
407+
408+
it('should put resolve data on scope even if there is no controller', inject(function ($state, $q, $timeout) {
409+
var views = {
410+
"": {
411+
template: '{{$resolve.user}}'
412+
}
413+
};
414+
var state = angular.extend({}, _state, { views: views });
415+
$stateProvider.state(state);
416+
elem.append($compile('<div><ui-view></ui-view></div>')(scope));
417+
418+
$state.transitionTo('resolve'); $q.flush(); $timeout.flush();
419+
expect(elem.text()).toBe('joeschmoe');
420+
}));
421+
});
422+
341423
describe('play nicely with other directives', function() {
342424
// related to issue #857
343425
it('should work with ngIf', inject(function ($state, $q, $compile) {

0 commit comments

Comments
 (0)