Skip to content

Commit fde64e1

Browse files
feat(uiView): Put $animate promises on element.data('$uiView')
closes #2562 closes #2579
1 parent eb68955 commit fde64e1

File tree

2 files changed

+78
-26
lines changed

2 files changed

+78
-26
lines changed

src/viewDirective.js

+36-25
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,26 @@
2626
* functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
2727
*
2828
* @param {string=} onload Expression to evaluate whenever the view updates.
29-
*
29+
*
3030
* @example
31-
* A view can be unnamed or named.
31+
* A view can be unnamed or named.
3232
* <pre>
3333
* <!-- Unnamed -->
34-
* <div ui-view></div>
35-
*
34+
* <div ui-view></div>
35+
*
3636
* <!-- Named -->
3737
* <div ui-view="viewName"></div>
3838
* </pre>
3939
*
40-
* You can only have one unnamed view within any template (or root html). If you are only using a
40+
* You can only have one unnamed view within any template (or root html). If you are only using a
4141
* single view and it is unnamed then you can populate it like so:
4242
* <pre>
43-
* <div ui-view></div>
43+
* <div ui-view></div>
4444
* $stateProvider.state("home", {
4545
* template: "<h1>HELLO!</h1>"
4646
* })
4747
* </pre>
48-
*
48+
*
4949
* The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#methods_state `views`}
5050
* config property, by name, in this case an empty name:
5151
* <pre>
@@ -57,13 +57,13 @@
5757
* }
5858
* })
5959
* </pre>
60-
*
61-
* But typically you'll only use the views property if you name your view or have more than one view
62-
* in the same template. There's not really a compelling reason to name a view if its the only one,
60+
*
61+
* But typically you'll only use the views property if you name your view or have more than one view
62+
* in the same template. There's not really a compelling reason to name a view if its the only one,
6363
* but you could if you wanted, like so:
6464
* <pre>
6565
* <div ui-view="main"></div>
66-
* </pre>
66+
* </pre>
6767
* <pre>
6868
* $stateProvider.state("home", {
6969
* views: {
@@ -73,14 +73,14 @@
7373
* }
7474
* })
7575
* </pre>
76-
*
76+
*
7777
* Really though, you'll use views to set up multiple views:
7878
* <pre>
7979
* <div ui-view></div>
80-
* <div ui-view="chart"></div>
81-
* <div ui-view="data"></div>
80+
* <div ui-view="chart"></div>
81+
* <div ui-view="data"></div>
8282
* </pre>
83-
*
83+
*
8484
* <pre>
8585
* $stateProvider.state("home", {
8686
* views: {
@@ -111,8 +111,8 @@
111111
* <ui-view autoscroll='scopeVariable'/>
112112
* </pre>
113113
*/
114-
$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
115-
function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) {
114+
$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate', '$q'];
115+
function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate, $q) {
116116

117117
function getService() {
118118
return ($injector.has) ? function(service) {
@@ -181,7 +181,8 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
181181
var previousEl, currentEl, currentScope, latestLocals,
182182
onloadExp = attrs.onload || '',
183183
autoScrollExp = attrs.autoscroll,
184-
renderer = getRenderer(attrs, scope);
184+
renderer = getRenderer(attrs, scope),
185+
inherited = $element.inheritedData('$uiView');
185186

186187
scope.$on('$stateChangeSuccess', function() {
187188
updateView(false);
@@ -201,7 +202,9 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
201202
}
202203

203204
if (currentEl) {
205+
var $uiViewData = currentEl.data('$uiView');
204206
renderer.leave(currentEl, function() {
207+
$uiViewData.$$animLeave.resolve();
205208
previousEl = null;
206209
});
207210

@@ -212,7 +215,7 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
212215

213216
function updateView(firstTime) {
214217
var newScope,
215-
name = getUiViewName(scope, attrs, $element, $interpolate),
218+
name = getUiViewName(scope, attrs, inherited, $interpolate),
216219
previousLocals = name && $state.$current && $state.$current.locals[name];
217220

218221
if (!firstTime && previousLocals === latestLocals) return; // nothing to do
@@ -234,7 +237,16 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
234237
newScope.$emit('$viewContentLoading', name);
235238

236239
var clone = $transclude(newScope, function(clone) {
237-
renderer.enter(clone, $element, function onUiViewEnter() {
240+
var animEnter = $q.defer(), animLeave = $q.defer();
241+
var viewData = {
242+
name: name,
243+
$animEnter: animEnter.promise,
244+
$animLeave: animLeave.promise,
245+
$$animLeave: animLeave
246+
};
247+
248+
renderer.enter(clone.data('$uiView', viewData), $element, function onUiViewEnter() {
249+
animEnter.resolve();
238250
if(currentScope) {
239251
currentScope.$emit('$viewContentAnimationEnded');
240252
}
@@ -278,14 +290,14 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
278290
var initial = tElement.html();
279291
return function (scope, $element, attrs) {
280292
var current = $state.$current,
281-
name = getUiViewName(scope, attrs, $element, $interpolate),
282-
locals = current && current.locals[name];
293+
$uiViewData = $element.data('$uiView'),
294+
locals = current && current.locals[$uiViewData.name];
283295

284296
if (! locals) {
285297
return;
286298
}
287299

288-
$element.data('$uiView', { name: name, state: locals.$$state });
300+
extend($uiViewData, { state: locals.$$state });
289301
$element.html(locals.$template ? locals.$template : initial);
290302

291303
var link = $compile($element.contents());
@@ -311,9 +323,8 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
311323
* Shared ui-view code for both directives:
312324
* Given scope, element, and its attributes, return the view's name
313325
*/
314-
function getUiViewName(scope, attrs, element, $interpolate) {
326+
function getUiViewName(scope, attrs, inherited, $interpolate) {
315327
var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
316-
var inherited = element.inheritedData('$uiView');
317328
return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
318329
}
319330

test/viewDirectiveSpec.js

+42-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ function animateFlush($animate) {
99
describe('uiView', function () {
1010
'use strict';
1111

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

1414
beforeEach(function() {
1515
var depends = ['ui.router'];
16+
log = "";
1617

1718
try {
1819
angular.module('ngAnimate');
@@ -101,6 +102,16 @@ describe('uiView', function () {
101102
template: 'view3'
102103
}
103104
}
105+
},
106+
nState = {
107+
template: 'nState',
108+
controller: function ($scope, $element) {
109+
var data = $element.data('$uiView');
110+
$scope.$on("$destroy", function() { log += 'destroy;'});
111+
data.$animEnter.then(function() { log += "animEnter;"});
112+
data.$animLeave.then(function() {
113+
log += "animLeave;"});
114+
}
104115
};
105116

106117
beforeEach(module(function ($stateProvider) {
@@ -117,6 +128,7 @@ describe('uiView', function () {
117128
.state('j', jState)
118129
.state('k', kState)
119130
.state('l', lState)
131+
.state('n', nState)
120132
}));
121133

122134
beforeEach(inject(function ($rootScope, _$compile_) {
@@ -560,5 +572,34 @@ describe('uiView', function () {
560572
// No more animations
561573
expect($animate.queue.length).toBe(0);
562574
}));
575+
576+
it ('should expose animation promises to controllers', inject(function($state, $q, $compile, $animate, $rootScope) {
577+
$rootScope.$on('$stateChangeStart', function(evt, toState) {
578+
log += 'start:' + toState.name + ';';
579+
});
580+
$rootScope.$on('$stateChangeSuccess', function(evt, toState) {
581+
log += 'success:' + toState.name + ';';
582+
});
583+
584+
var content = 'Initial Content';
585+
elem.append($compile('<div><ui-view>' + content + '</ui-view></div>')(scope));
586+
$state.transitionTo('n');
587+
$q.flush();
588+
589+
expect($state.current.name).toBe('n');
590+
expect(log).toBe('start:n;success:n;');
591+
592+
animateFlush($animate);
593+
expect(log).toBe('start:n;success:n;animEnter;');
594+
595+
$state.transitionTo('a');
596+
$q.flush();
597+
expect($state.current.name).toBe('a');
598+
expect(log).toBe('start:n;success:n;animEnter;start:a;success:a;destroy;');
599+
600+
animateFlush($animate);
601+
expect(log).toBe('start:n;success:n;animEnter;start:a;success:a;destroy;animLeave;');
602+
}));
603+
563604
});
564605
});

0 commit comments

Comments
 (0)