Skip to content

Commit 58eef1f

Browse files
Merge pull request #2596 from christopherthielen/master
Expose animation promises as element data
2 parents 2a20a96 + a5578de commit 58eef1f

File tree

2 files changed

+63
-116
lines changed

2 files changed

+63
-116
lines changed

src/ng1/viewDirective.ts

+23-46
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
var ngMajorVer = angular.version.major;
2-
var ngMinorVer = angular.version.minor;
31
/** @module view */ /** for typedoc */
42
import {extend} from "../common/common";
53
import {isDefined} from "../common/predicates";
@@ -31,9 +29,6 @@ import {UIViewData} from "../view/interface";
3129
* service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
3230
* scroll ui-view elements into view when they are populated during a state activation.
3331
*
34-
* @param {string=} noanimation If truthy, the non-animated renderer will be selected (no animations
35-
* will be applied to the ui-view)
36-
*
3732
* *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
3833
* functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
3934
*
@@ -127,26 +122,16 @@ $ViewDirective.$inject = ['$view', '$animate', '$uiViewScroll', '$interpolate',
127122
function $ViewDirective( $view, $animate, $uiViewScroll, $interpolate, $q) {
128123

129124
function getRenderer(attrs, scope) {
130-
131-
function animEnabled(element) {
132-
if (!!attrs.noanimation) return false;
133-
return (ngMajorVer === 1 && ngMinorVer >= 4) ? !!$animate.enabled(element) : !!$animate.enabled();
134-
}
135-
136125
return {
137126
enter: function(element, target, cb) {
138-
if (!animEnabled(element)) {
139-
target.after(element); cb();
140-
} else if (angular.version.minor > 2) {
127+
if (angular.version.minor > 2) {
141128
$animate.enter(element, null, target).then(cb);
142129
} else {
143130
$animate.enter(element, null, target, cb);
144131
}
145132
},
146133
leave: function(element, cb) {
147-
if (!animEnabled(element)) {
148-
element.remove(); cb();
149-
} else if (angular.version.minor > 2) {
134+
if (angular.version.minor > 2) {
150135
$animate.leave(element).then(cb);
151136
} else {
152137
$animate.leave(element, cb);
@@ -192,7 +177,7 @@ function $ViewDirective( $view, $animate, $uiViewScroll, $interpolate,
192177
trace.traceUiViewEvent("Linking", viewData);
193178

194179
function configUpdatedCallback(config?: ViewConfig) {
195-
if (configsEqual(viewConfig, config) || scope._willBeDestroyed) return;
180+
if (configsEqual(viewConfig, config)) return;
196181
trace.traceUiViewConfigUpdated(viewData, config && config.context);
197182

198183
viewConfig = config;
@@ -210,59 +195,51 @@ function $ViewDirective( $view, $animate, $uiViewScroll, $interpolate,
210195
});
211196

212197
function cleanupLastView() {
213-
var _previousEl = previousEl;
214-
var _currentScope = currentScope;
215-
216-
if (_currentScope) {
217-
_currentScope._willBeDestroyed = true;
198+
if (previousEl) {
199+
trace.traceUiViewEvent("Removing (previous) el", previousEl.data('$uiView'));
200+
previousEl.remove();
201+
previousEl = null;
218202
}
219203

220-
function cleanOld() {
221-
if (_previousEl) {
222-
trace.traceUiViewEvent("Removing (previous) el", viewData);
223-
_previousEl.remove();
224-
_previousEl = null;
225-
}
226-
227-
if (_currentScope) {
228-
trace.traceUiViewEvent("Destroying (previous) scope", viewData);
229-
_currentScope.$destroy();
230-
_currentScope = null;
231-
}
204+
if (currentScope) {
205+
trace.traceUiViewEvent("Destroying scope", viewData);
206+
currentScope.$destroy();
207+
currentScope = null;
232208
}
233209

234210
if (currentEl) {
235-
trace.traceUiViewEvent("Animate out (previous)", viewData);
211+
let _viewData = currentEl.data('$uiView');
212+
trace.traceUiViewEvent("Animate out", _viewData);
236213
renderer.leave(currentEl, function() {
237-
cleanOld();
214+
_viewData.$$animLeave.resolve();
238215
previousEl = null;
239216
});
240217

241218
previousEl = currentEl;
242-
} else {
243-
cleanOld();
244-
previousEl = null;
219+
currentEl = null;
245220
}
246-
247-
currentEl = null;
248-
currentScope = null;
249221
}
250222

251223
function updateView(config?: ViewConfig) {
252224
config = config || <any> {};
253225
let newScope = scope.$new();
254226
trace.traceUiViewScopeCreated(viewData, newScope);
227+
let animEnter = $q.defer(), animLeave = $q.defer();
255228

256-
extend(viewData, {
229+
let $uiViewData = extend({}, viewData, {
257230
context: config.context,
258231
$template: config.template,
259232
$controller: config.controller,
260233
$controllerAs: config.controllerAs,
261-
$locals: config.locals
234+
$locals: config.locals,
235+
$animEnter: animEnter.promise,
236+
$animLeave: animLeave.promise,
237+
$$animLeave: animLeave
262238
});
263239

264240
let cloned = $transclude(newScope, function(clone) {
265-
renderer.enter(clone.data('$uiView', viewData), $element, function onUiViewEnter() {
241+
renderer.enter(clone.data('$uiView', $uiViewData), $element, function onUiViewEnter() {
242+
animEnter.resolve();
266243
if (currentScope) {
267244
currentScope.$emit('$viewContentAnimationEnded');
268245
}

test/viewDirectiveSpec.js

+40-70
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@ function animateFlush($animate) {
66
$animate && $animate.flush && $animate.flush(); // 1.4
77
}
88

9-
function animateFlush($animate) {
10-
$animate && $animate.triggerCallbacks && $animate.triggerCallbacks(); // 1.2-1.3
11-
$animate && $animate.flush && $animate.flush(); // 1.4
12-
}
13-
149
describe('uiView', function () {
1510
'use strict';
1611

17-
var log, scope, $compile, elem;
12+
var scope, $compile, elem, log;
1813

1914
beforeEach(function() {
20-
var depends = ['ui.router', 'ui.router.state.events'];
15+
var depends = ['ui.router'];
16+
log = "";
2117

2218
try {
2319
angular.module('ngAnimate');
@@ -31,17 +27,12 @@ describe('uiView', function () {
3127
module('ui.router.test');
3228
});
3329

34-
beforeEach(module(function ($provide, $stateEventsProvider) {
35-
$stateEventsProvider.enable();
30+
beforeEach(module(function ($provide) {
3631
$provide.decorator('$uiViewScroll', function () {
3732
return jasmine.createSpy('$uiViewScroll');
3833
});
3934
}));
4035

41-
beforeEach(function() {
42-
log = '';
43-
});
44-
4536
var aState = {
4637
template: 'aState template'
4738
},
@@ -113,12 +104,20 @@ describe('uiView', function () {
113104
}
114105
}
115106
},
116-
117-
oState = {
118-
template: 'oState',
107+
mState = {
108+
template: 'mState',
119109
controller: function ($scope, $element) {
120110
$scope.elementId = $element.attr('id');
121111
}
112+
},
113+
nState = {
114+
template: 'nState',
115+
controller: function ($scope, $element) {
116+
var data = $element.data('$uiView');
117+
$scope.$on("$destroy", function() { log += 'destroy;'});
118+
data.$animEnter.then(function() { log += "animEnter;"});
119+
data.$animLeave.then(function() { log += "animLeave;"});
120+
}
122121
};
123122

124123
beforeEach(module(function ($stateProvider) {
@@ -135,18 +134,8 @@ describe('uiView', function () {
135134
.state('j', jState)
136135
.state('k', kState)
137136
.state('l', lState)
138-
.state('m', {
139-
template: 'mState',
140-
controller: function($scope) {
141-
log += 'ctrl(m);';
142-
$scope.$on('$destroy', function() { log += '$destroy(m);'; });
143-
}
144-
})
145-
.state('n', {
146-
template: 'nState',
147-
controller: function($scope) { log += 'ctrl(n);'; }
148-
})
149-
.state('o', oState)
137+
.state('m', mState)
138+
.state('n', nState)
150139
}));
151140

152141
beforeEach(inject(function ($rootScope, _$compile_) {
@@ -342,11 +331,11 @@ describe('uiView', function () {
342331
}));
343332

344333
it('should instantiate a controller with both $scope and $element injections', inject(function ($state, $q) {
345-
elem.append($compile('<div><ui-view id="oState">{{elementId}}</ui-view></div>')(scope));
346-
$state.transitionTo(oState);
334+
elem.append($compile('<div><ui-view id="mState">{{elementId}}</ui-view></div>')(scope));
335+
$state.transitionTo(mState);
347336
$q.flush();
348337

349-
expect(elem.text()).toBe('oState');
338+
expect(elem.text()).toBe('mState');
350339
}));
351340

352341
describe('play nicely with other directives', function() {
@@ -596,52 +585,33 @@ describe('uiView', function () {
596585
expect($animate.queue.length).toBe(0);
597586
}));
598587

599-
it ('should disable animations if noanimation="true" is present', inject(function($state, $q, $compile, $animate) {
600-
var content = 'Initial Content', animation;
601-
elem.append($compile('<div><ui-view noanimation="true">' + content + '</ui-view></div>')(scope));
588+
it ('should expose animation promises to controllers', inject(function($state, $q, $compile, $animate, $transitions) {
589+
$transitions.onStart({}, function($transition$) { log += 'start:' + $transition$.to().name + ';'; });
590+
$transitions.onFinish({}, function($transition$) { log += 'finish:' + $transition$.to().name + ';'; });
591+
$transitions.onSuccess({}, function($transition$) { log += 'success:' + $transition$.to().name + ';'; });
602592

603-
animation = $animate.queue.shift();
604-
expect(animation).toBeUndefined();
605-
606-
$state.transitionTo(aState);
607-
$q.flush();
608-
animation = $animate.queue.shift();
609-
expect(animation).toBeUndefined();
610-
expect(elem.text()).toBe(aState.template);
611-
612-
$state.transitionTo(bState);
593+
var content = 'Initial Content';
594+
elem.append($compile('<div><ui-view>' + content + '</ui-view></div>')(scope));
595+
$state.transitionTo('n');
613596
$q.flush();
614-
animation = $animate.queue.shift();
615-
expect(animation).toBeUndefined();
616-
expect(elem.text()).toBe(bState.template);
617-
}));
618597

619-
describe('$destroy event', function() {
620-
it('is triggered after animation ends', inject(function($state, $q, $animate, $rootScope) {
621-
elem.append($compile('<div><ui-view></ui-view></div>')(scope));
598+
expect($state.current.name).toBe('n');
599+
expect(log).toBe('start:n;finish:n;success:n;');
622600

623-
$state.transitionTo('m');
624-
$q.flush();
625-
expect(log).toBe('ctrl(m);');
626-
$state.transitionTo('n');
627-
$q.flush();
601+
animateFlush($animate);
602+
$q.flush();
603+
expect(log).toBe('start:n;finish:n;success:n;animEnter;');
628604

629-
expect(log).toBe('ctrl(m);ctrl(n);');
630-
animateFlush($animate);
631-
expect(log).toBe('ctrl(m);ctrl(n);$destroy(m);');
632-
}));
605+
$state.transitionTo('a');
606+
$q.flush();
607+
expect($state.current.name).toBe('a');
608+
expect(log).toBe('start:n;finish:n;success:n;animEnter;start:a;finish:a;destroy;success:a;');
633609

634-
it('is triggered before $stateChangeSuccess if noanimation is present', inject(function($state, $q, $animate, $rootScope) {
635-
elem.append($compile('<div><ui-view noanimation="true"></ui-view></div>')(scope));
610+
animateFlush($animate);
611+
$q.flush();
612+
expect(log).toBe('start:n;finish:n;success:n;animEnter;start:a;finish:a;destroy;success:a;animLeave;');
613+
}));
636614

637-
$state.transitionTo('m');
638-
$q.flush();
639-
expect(log).toBe('ctrl(m);');
640-
$state.transitionTo('n');
641-
$q.flush();
642-
expect(log).toBe('ctrl(m);$destroy(m);ctrl(n);');
643-
}));
644-
});
645615
});
646616
});
647617

0 commit comments

Comments
 (0)