Skip to content

Commit

Permalink
Merge pull request #2596 from christopherthielen/master
Browse files Browse the repository at this point in the history
Expose animation promises as element data
  • Loading branch information
christopherthielen committed Mar 1, 2016
2 parents 2a20a96 + a5578de commit 58eef1f
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 116 deletions.
69 changes: 23 additions & 46 deletions src/ng1/viewDirective.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
var ngMajorVer = angular.version.major;
var ngMinorVer = angular.version.minor;
/** @module view */ /** for typedoc */
import {extend} from "../common/common";
import {isDefined} from "../common/predicates";
Expand Down Expand Up @@ -31,9 +29,6 @@ import {UIViewData} from "../view/interface";
* service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
* scroll ui-view elements into view when they are populated during a state activation.
*
* @param {string=} noanimation If truthy, the non-animated renderer will be selected (no animations
* will be applied to the ui-view)
*
* *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
* functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
*
Expand Down Expand Up @@ -127,26 +122,16 @@ $ViewDirective.$inject = ['$view', '$animate', '$uiViewScroll', '$interpolate',
function $ViewDirective( $view, $animate, $uiViewScroll, $interpolate, $q) {

function getRenderer(attrs, scope) {

function animEnabled(element) {
if (!!attrs.noanimation) return false;
return (ngMajorVer === 1 && ngMinorVer >= 4) ? !!$animate.enabled(element) : !!$animate.enabled();
}

return {
enter: function(element, target, cb) {
if (!animEnabled(element)) {
target.after(element); cb();
} else if (angular.version.minor > 2) {
if (angular.version.minor > 2) {
$animate.enter(element, null, target).then(cb);
} else {
$animate.enter(element, null, target, cb);
}
},
leave: function(element, cb) {
if (!animEnabled(element)) {
element.remove(); cb();
} else if (angular.version.minor > 2) {
if (angular.version.minor > 2) {
$animate.leave(element).then(cb);
} else {
$animate.leave(element, cb);
Expand Down Expand Up @@ -192,7 +177,7 @@ function $ViewDirective( $view, $animate, $uiViewScroll, $interpolate,
trace.traceUiViewEvent("Linking", viewData);

function configUpdatedCallback(config?: ViewConfig) {
if (configsEqual(viewConfig, config) || scope._willBeDestroyed) return;
if (configsEqual(viewConfig, config)) return;
trace.traceUiViewConfigUpdated(viewData, config && config.context);

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

function cleanupLastView() {
var _previousEl = previousEl;
var _currentScope = currentScope;

if (_currentScope) {
_currentScope._willBeDestroyed = true;
if (previousEl) {
trace.traceUiViewEvent("Removing (previous) el", previousEl.data('$uiView'));
previousEl.remove();
previousEl = null;
}

function cleanOld() {
if (_previousEl) {
trace.traceUiViewEvent("Removing (previous) el", viewData);
_previousEl.remove();
_previousEl = null;
}

if (_currentScope) {
trace.traceUiViewEvent("Destroying (previous) scope", viewData);
_currentScope.$destroy();
_currentScope = null;
}
if (currentScope) {
trace.traceUiViewEvent("Destroying scope", viewData);
currentScope.$destroy();
currentScope = null;
}

if (currentEl) {
trace.traceUiViewEvent("Animate out (previous)", viewData);
let _viewData = currentEl.data('$uiView');
trace.traceUiViewEvent("Animate out", _viewData);
renderer.leave(currentEl, function() {
cleanOld();
_viewData.$$animLeave.resolve();
previousEl = null;
});

previousEl = currentEl;
} else {
cleanOld();
previousEl = null;
currentEl = null;
}

currentEl = null;
currentScope = null;
}

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

extend(viewData, {
let $uiViewData = extend({}, viewData, {
context: config.context,
$template: config.template,
$controller: config.controller,
$controllerAs: config.controllerAs,
$locals: config.locals
$locals: config.locals,
$animEnter: animEnter.promise,
$animLeave: animLeave.promise,
$$animLeave: animLeave
});

let cloned = $transclude(newScope, function(clone) {
renderer.enter(clone.data('$uiView', viewData), $element, function onUiViewEnter() {
renderer.enter(clone.data('$uiView', $uiViewData), $element, function onUiViewEnter() {
animEnter.resolve();
if (currentScope) {
currentScope.$emit('$viewContentAnimationEnded');
}
Expand Down
110 changes: 40 additions & 70 deletions test/viewDirectiveSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,14 @@ function animateFlush($animate) {
$animate && $animate.flush && $animate.flush(); // 1.4
}

function animateFlush($animate) {
$animate && $animate.triggerCallbacks && $animate.triggerCallbacks(); // 1.2-1.3
$animate && $animate.flush && $animate.flush(); // 1.4
}

describe('uiView', function () {
'use strict';

var log, scope, $compile, elem;
var scope, $compile, elem, log;

beforeEach(function() {
var depends = ['ui.router', 'ui.router.state.events'];
var depends = ['ui.router'];
log = "";

try {
angular.module('ngAnimate');
Expand All @@ -31,17 +27,12 @@ describe('uiView', function () {
module('ui.router.test');
});

beforeEach(module(function ($provide, $stateEventsProvider) {
$stateEventsProvider.enable();
beforeEach(module(function ($provide) {
$provide.decorator('$uiViewScroll', function () {
return jasmine.createSpy('$uiViewScroll');
});
}));

beforeEach(function() {
log = '';
});

var aState = {
template: 'aState template'
},
Expand Down Expand Up @@ -113,12 +104,20 @@ describe('uiView', function () {
}
}
},

oState = {
template: 'oState',
mState = {
template: 'mState',
controller: function ($scope, $element) {
$scope.elementId = $element.attr('id');
}
},
nState = {
template: 'nState',
controller: function ($scope, $element) {
var data = $element.data('$uiView');
$scope.$on("$destroy", function() { log += 'destroy;'});
data.$animEnter.then(function() { log += "animEnter;"});
data.$animLeave.then(function() { log += "animLeave;"});
}
};

beforeEach(module(function ($stateProvider) {
Expand All @@ -135,18 +134,8 @@ describe('uiView', function () {
.state('j', jState)
.state('k', kState)
.state('l', lState)
.state('m', {
template: 'mState',
controller: function($scope) {
log += 'ctrl(m);';
$scope.$on('$destroy', function() { log += '$destroy(m);'; });
}
})
.state('n', {
template: 'nState',
controller: function($scope) { log += 'ctrl(n);'; }
})
.state('o', oState)
.state('m', mState)
.state('n', nState)
}));

beforeEach(inject(function ($rootScope, _$compile_) {
Expand Down Expand Up @@ -342,11 +331,11 @@ describe('uiView', function () {
}));

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

expect(elem.text()).toBe('oState');
expect(elem.text()).toBe('mState');
}));

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

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

animation = $animate.queue.shift();
expect(animation).toBeUndefined();

$state.transitionTo(aState);
$q.flush();
animation = $animate.queue.shift();
expect(animation).toBeUndefined();
expect(elem.text()).toBe(aState.template);

$state.transitionTo(bState);
var content = 'Initial Content';
elem.append($compile('<div><ui-view>' + content + '</ui-view></div>')(scope));
$state.transitionTo('n');
$q.flush();
animation = $animate.queue.shift();
expect(animation).toBeUndefined();
expect(elem.text()).toBe(bState.template);
}));

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

$state.transitionTo('m');
$q.flush();
expect(log).toBe('ctrl(m);');
$state.transitionTo('n');
$q.flush();
animateFlush($animate);
$q.flush();
expect(log).toBe('start:n;finish:n;success:n;animEnter;');

expect(log).toBe('ctrl(m);ctrl(n);');
animateFlush($animate);
expect(log).toBe('ctrl(m);ctrl(n);$destroy(m);');
}));
$state.transitionTo('a');
$q.flush();
expect($state.current.name).toBe('a');
expect(log).toBe('start:n;finish:n;success:n;animEnter;start:a;finish:a;destroy;success:a;');

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

$state.transitionTo('m');
$q.flush();
expect(log).toBe('ctrl(m);');
$state.transitionTo('n');
$q.flush();
expect(log).toBe('ctrl(m);$destroy(m);ctrl(n);');
}));
});
});
});

Expand Down

0 comments on commit 58eef1f

Please sign in to comment.