Skip to content

Commit d3502f3

Browse files
fix(uiView): separate $uiView and $uiViewAnim element.data()
The `$uiView` element.data lifecycle is somewhat fragile. The Fill directive reads the inherited `$uiView` data to figure out which state the ui-view was created in. In a previous commit, the non-fill directive was setting the `$uiView` date earlier to hold the animation promises. This caused the fill directive to read the wrong state data, breaking some users' apps. BC-BREAK Users who were using `element.data('$uiView').$animEnter` or `$animLeave` should now use `element.data('$uiViewAnim').$animEnter` instead Closes #2763
1 parent ca372ca commit d3502f3

File tree

2 files changed

+30
-11
lines changed

2 files changed

+30
-11
lines changed

src/viewDirective.js

+11-10
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate,
221221
}
222222

223223
if (currentEl) {
224-
var $uiViewData = currentEl.data('$uiView');
224+
var $uiViewData = currentEl.data('$uiViewAnim');
225225
renderer.leave(currentEl, function() {
226226
$uiViewData.$$animLeave.resolve();
227227
previousEl = null;
@@ -234,7 +234,7 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate,
234234

235235
function updateView(firstTime) {
236236
var newScope,
237-
name = getUiViewName(scope, attrs, inherited, $interpolate),
237+
name = getUiViewName(scope, attrs, $element, $interpolate),
238238
previousLocals = name && $state.$current && $state.$current.locals[name];
239239

240240
if (!firstTime && previousLocals === latestLocals) return; // nothing to do
@@ -257,14 +257,14 @@ function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate,
257257

258258
var clone = $transclude(newScope, function(clone) {
259259
var animEnter = $q.defer(), animLeave = $q.defer();
260-
var viewData = {
261-
name: name,
260+
var viewAnimData = {
262261
$animEnter: animEnter.promise,
263262
$animLeave: animLeave.promise,
264263
$$animLeave: animLeave
265264
};
266265

267-
renderer.enter(clone.data('$uiView', viewData), $element, function onUiViewEnter() {
266+
clone.data('$uiViewAnim', viewAnimData);
267+
renderer.enter(clone, $element, function onUiViewEnter() {
268268
animEnter.resolve();
269269
if(currentScope) {
270270
currentScope.$emit('$viewContentAnimationEnded');
@@ -309,14 +309,14 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
309309
var initial = tElement.html();
310310
return function (scope, $element, attrs) {
311311
var current = $state.$current,
312-
$uiViewData = $element.data('$uiView'),
313-
locals = current && current.locals[$uiViewData.name];
312+
name = getUiViewName(scope, attrs, $element, $interpolate),
313+
locals = current && current.locals[name];
314314

315315
if (! locals) {
316316
return;
317317
}
318318

319-
extend($uiViewData, { state: locals.$$state });
319+
$element.data('$uiView', { name: name, state: locals.$$state });
320320
$element.html(locals.$template ? locals.$template : initial);
321321

322322
var resolveData = angular.extend({}, locals);
@@ -347,9 +347,10 @@ function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate
347347
* Shared ui-view code for both directives:
348348
* Given scope, element, and its attributes, return the view's name
349349
*/
350-
function getUiViewName(scope, attrs, inherited, $interpolate) {
350+
function getUiViewName(scope, attrs, element, $interpolate) {
351351
var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
352-
return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
352+
var uiViewCreatedBy = element.inheritedData('$uiView');
353+
return name.indexOf('@') >= 0 ? name : (name + '@' + (uiViewCreatedBy ? uiViewCreatedBy.state.name : ''));
353354
}
354355

355356
angular.module('ui.router.state').directive('uiView', $ViewDirective);

test/viewDirectiveSpec.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ describe('uiView', function () {
112112
nState = {
113113
template: 'nState',
114114
controller: function ($scope, $element) {
115-
var data = $element.data('$uiView');
115+
var data = $element.data('$uiViewAnim');
116116
$scope.$on("$destroy", function() { log += 'destroy;'});
117117
data.$animEnter.then(function() { log += "animEnter;"});
118118
data.$animLeave.then(function() {
@@ -715,3 +715,21 @@ describe('uiView', function () {
715715

716716
});
717717
});
718+
719+
describe("UiView", function() {
720+
beforeEach(module('ui.router'));
721+
beforeEach(module(function($stateProvider) {
722+
$stateProvider
723+
.state('main', { abstract: true, views: { main: {} } })
724+
.state('main.home', { views: { content: { template: 'home.html' } } });
725+
}));
726+
727+
it("shouldn't puke on weird view setups", inject(function($compile, $rootScope, $q, $state) {
728+
$compile('<div ui-view="main"><div ui-view="content"></div></div>')($rootScope);
729+
730+
$state.go('main.home');
731+
$q.flush();
732+
733+
expect($state.current.name).toBe('main.home');
734+
}));
735+
});

0 commit comments

Comments
 (0)