diff --git a/src/viewDirective.js b/src/viewDirective.js index 8c91c86bd..4d8e66d41 100644 --- a/src/viewDirective.js +++ b/src/viewDirective.js @@ -1,5 +1,3 @@ -var ngMajorVer = angular.version.major; -var ngMinorVer = angular.version.minor; /** * @ngdoc directive * @name ui.router.state.directive:ui-view @@ -24,33 +22,30 @@ var ngMinorVer = angular.version.minor; * 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()`.* * * @param {string=} onload Expression to evaluate whenever the view updates. - * + * * @example - * A view can be unnamed or named. + * A view can be unnamed or named. *
* - * - * + * + * * * ** - * You can only have one unnamed view within any template (or root html). If you are only using a + * You can only have one unnamed view within any template (or root html). If you are only using a * single view and it is unnamed then you can populate it like so: *
- *
+ *
* $stateProvider.state("home", {
* template: "HELLO!
"
* })
*
- *
+ *
* The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#methods_state `views`}
* config property, by name, in this case an empty name:
* @@ -62,13 +57,13 @@ var ngMinorVer = angular.version.minor; * } * }) *- * - * But typically you'll only use the views property if you name your view or have more than one view - * in the same template. There's not really a compelling reason to name a view if its the only one, + * + * But typically you'll only use the views property if you name your view or have more than one view + * in the same template. There's not really a compelling reason to name a view if its the only one, * but you could if you wanted, like so: *
* - *+ * *
* $stateProvider.state("home", {
* views: {
@@ -78,14 +73,14 @@ var ngMinorVer = angular.version.minor;
* }
* })
*
- *
+ *
* Really though, you'll use views to set up multiple views:
* * - * - * + * + * *- * + * *
* $stateProvider.state("home", {
* views: {
@@ -116,8 +111,8 @@ var ngMinorVer = angular.version.minor;
*
*
*/
-$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
-function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) {
+$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate', '$q'];
+function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate, $q) {
function getService() {
return ($injector.has) ? function(service) {
@@ -138,35 +133,24 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
// Returns a set of DOM manipulation functions based on which Angular version
// it should use
function getRenderer(attrs, scope) {
- var statics = {
- enter: function (element, target, cb) { target.after(element); cb(); },
- leave: function (element, cb) { element.remove(); cb(); }
+ var statics = function() {
+ return {
+ enter: function (element, target, cb) { target.after(element); cb(); },
+ leave: function (element, cb) { element.remove(); cb(); }
+ };
};
- if (!!attrs.noanimation) return statics;
-
- function animEnabled(element) {
- if (ngMajorVer === 1 && ngMinorVer >= 4) return !!$animate.enabled(element);
- if (ngMajorVer === 1 && ngMinorVer >= 2) return !!$animate.enabled();
- return (!!$animator);
- }
-
- // ng 1.2+
if ($animate) {
return {
enter: function(element, target, cb) {
- if (!animEnabled(element)) {
- statics.enter(element, target, 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)) {
- statics.leave(element, cb);
- } else if (angular.version.minor > 2) {
+ if (angular.version.minor > 2) {
$animate.leave(element).then(cb);
} else {
$animate.leave(element, cb);
@@ -175,7 +159,6 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
};
}
- // ng 1.1.5
if ($animator) {
var animate = $animator && $animator(scope, attrs);
@@ -185,7 +168,7 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
};
}
- return statics;
+ return statics();
}
var directive = {
@@ -198,7 +181,8 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
var previousEl, currentEl, currentScope, latestLocals,
onloadExp = attrs.onload || '',
autoScrollExp = attrs.autoscroll,
- renderer = getRenderer(attrs, scope);
+ renderer = getRenderer(attrs, scope),
+ inherited = $element.inheritedData('$uiView');
scope.$on('$stateChangeSuccess', function() {
updateView(false);
@@ -207,45 +191,34 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
updateView(true);
function cleanupLastView() {
- var _previousEl = previousEl;
- var _currentScope = currentScope;
-
- if (_currentScope) {
- _currentScope._willBeDestroyed = true;
+ if (previousEl) {
+ previousEl.remove();
+ previousEl = null;
}
- function cleanOld() {
- if (_previousEl) {
- _previousEl.remove();
- }
-
- if (_currentScope) {
- _currentScope.$destroy();
- }
+ if (currentScope) {
+ currentScope.$destroy();
+ currentScope = null;
}
if (currentEl) {
+ var $uiViewData = currentEl.data('$uiView');
renderer.leave(currentEl, function() {
- cleanOld();
+ $uiViewData.$$animLeave.resolve();
previousEl = null;
});
previousEl = currentEl;
- } else {
- cleanOld();
- previousEl = null;
+ currentEl = null;
}
-
- currentEl = null;
- currentScope = null;
}
function updateView(firstTime) {
var newScope,
- name = getUiViewName(scope, attrs, $element, $interpolate),
+ name = getUiViewName(scope, attrs, inherited, $interpolate),
previousLocals = name && $state.$current && $state.$current.locals[name];
- if (!firstTime && previousLocals === latestLocals || scope._willBeDestroyed) return; // nothing to do
+ if (!firstTime && previousLocals === latestLocals) return; // nothing to do
newScope = scope.$new();
latestLocals = $state.$current.locals[name];
@@ -264,7 +237,16 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate)
newScope.$emit('$viewContentLoading', name);
var clone = $transclude(newScope, function(clone) {
- renderer.enter(clone, $element, function onUiViewEnter() {
+ var animEnter = $q.defer(), animLeave = $q.defer();
+ var viewData = {
+ name: name,
+ $animEnter: animEnter.promise,
+ $animLeave: animLeave.promise,
+ $$animLeave: animLeave
+ };
+
+ renderer.enter(clone.data('$uiView', viewData), $element, function onUiViewEnter() {
+ animEnter.resolve();
if(currentScope) {
currentScope.$emit('$viewContentAnimationEnded');
}
@@ -308,14 +290,14 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
var initial = tElement.html();
return function (scope, $element, attrs) {
var current = $state.$current,
- name = getUiViewName(scope, attrs, $element, $interpolate),
- locals = current && current.locals[name];
+ $uiViewData = $element.data('$uiView'),
+ locals = current && current.locals[$uiViewData.name];
if (! locals) {
return;
}
- $element.data('$uiView', { name: name, state: locals.$$state });
+ extend($uiViewData, { state: locals.$$state });
$element.html(locals.$template ? locals.$template : initial);
var link = $compile($element.contents());
@@ -341,9 +323,8 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
* Shared ui-view code for both directives:
* Given scope, element, and its attributes, return the view's name
*/
-function getUiViewName(scope, attrs, element, $interpolate) {
+function getUiViewName(scope, attrs, inherited, $interpolate) {
var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
- var inherited = element.inheritedData('$uiView');
return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
}
diff --git a/test/viewDirectiveSpec.js b/test/viewDirectiveSpec.js
index fd9566326..d9a27441f 100644
--- a/test/viewDirectiveSpec.js
+++ b/test/viewDirectiveSpec.js
@@ -9,10 +9,11 @@ function animateFlush($animate) {
describe('uiView', function () {
'use strict';
- var log, scope, $compile, elem;
+ var scope, $compile, elem, log;
beforeEach(function() {
var depends = ['ui.router'];
+ log = "";
try {
angular.module('ngAnimate');
@@ -32,10 +33,6 @@ describe('uiView', function () {
});
}));
- beforeEach(function() {
- log = '';
- });
-
var aState = {
template: 'aState template'
},
@@ -105,6 +102,16 @@ describe('uiView', function () {
template: 'view3'
}
}
+ },
+ 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) {
@@ -121,15 +128,7 @@ describe('uiView', function () {
.state('j', jState)
.state('k', kState)
.state('l', lState)
- .state('m', {
- controller: function($scope) {
- log += 'ctrl(m);';
- $scope.$on('$destroy', function() { log += '$destroy(m);'; });
- }
- })
- .state('n', {
- controller: function($scope) { log += 'ctrl(n);'; }
- });
+ .state('n', nState)
}));
beforeEach(inject(function ($rootScope, _$compile_) {
@@ -574,54 +573,32 @@ 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('