diff --git a/src/ng1/viewDirective.ts b/src/ng1/viewDirective.ts index 47bff217f..4c785488c 100644 --- a/src/ng1/viewDirective.ts +++ b/src/ng1/viewDirective.ts @@ -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"; @@ -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()`.* * @@ -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); @@ -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; @@ -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 || {}; 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'); } diff --git a/test/viewDirectiveSpec.js b/test/viewDirectiveSpec.js index a31dfecc4..4ea19818e 100644 --- a/test/viewDirectiveSpec.js +++ b/test/viewDirectiveSpec.js @@ -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'); @@ -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' }, @@ -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) { @@ -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_) { @@ -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('
{{elementId}}
')(scope)); - $state.transitionTo(oState); + elem.append($compile('
{{elementId}}
')(scope)); + $state.transitionTo(mState); $q.flush(); - expect(elem.text()).toBe('oState'); + expect(elem.text()).toBe('mState'); })); describe('play nicely with other directives', function() { @@ -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('
' + content + '
')(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('
' + content + '
')(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('
')(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('
')(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);'); - })); - }); }); });