Skip to content

Commit

Permalink
fix(uiView): separate $uiView and $uiViewAnim element.data()
Browse files Browse the repository at this point in the history
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

Manually merged/cherry picked from commit d3502f3
  • Loading branch information
christopherthielen committed Aug 31, 2016
1 parent 5638a65 commit a94117d
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 3 deletions.
12 changes: 10 additions & 2 deletions src/ng1/directives/viewDirective.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ import ITimeoutService = angular.ITimeoutService;
export type UIViewData = {
$cfg: Ng1ViewConfig;
$uiView: ActiveUIView;
}

/** @hidden */
export type UIViewAnimData = {
$animEnter: Promise<any>;
$animLeave: Promise<any>;
$$animLeave: { resolve: () => any; } // "deferred"
Expand Down Expand Up @@ -262,7 +265,7 @@ function $ViewDirective($view: ViewService, $animate: any, $uiViewScroll: any, $
}

if (currentEl) {
let _viewData = currentEl.data('$uiView');
let _viewData = currentEl.data('$uiViewAnim');
trace.traceUIViewEvent("Animate out", _viewData);
renderer.leave(currentEl, function() {
_viewData.$$animLeave.resolve();
Expand All @@ -281,13 +284,18 @@ function $ViewDirective($view: ViewService, $animate: any, $uiViewScroll: any, $
let $uiViewData: UIViewData = {
$cfg: config,
$uiView: activeUIView,
};

let $uiViewAnim: UIViewAnimData = {
$animEnter: animEnter.promise,
$animLeave: animLeave.promise,
$$animLeave: animLeave
};

let cloned = $transclude(newScope, function(clone) {
renderer.enter(clone.data('$uiView', $uiViewData), $element, function onUIViewEnter() {
clone.data('$uiViewAnim', $uiViewAnim);
clone.data('$uiView', $uiViewData);
renderer.enter(clone, $element, function onUIViewEnter() {
animEnter.resolve();
if (currentScope) currentScope.$emit('$viewContentAnimationEnded');

Expand Down
20 changes: 19 additions & 1 deletion test/ng1/viewDirectiveSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ describe('uiView', function () {
nState = {
template: 'nState',
controller: function ($scope, $element) {
var data = $element.data('$uiView');
var data = $element.data('$uiViewAnim');
$scope.$on("$destroy", function() { log += 'destroy;'});
data.$animEnter.then(function() { log += "animEnter;"});
data.$animLeave.then(function() { log += "animLeave;"});
Expand Down Expand Up @@ -708,6 +708,24 @@ describe('uiView', function () {
});
});

describe("UiView", function() {
beforeEach(module('ui.router'));
beforeEach(module(function($stateProvider) {
$stateProvider
.state('main', { abstract: true, views: { main: {} } })
.state('main.home', { views: { content: { template: 'home.html' } } });
}));

it("shouldn't puke on weird view setups", inject(function($compile, $rootScope, $q, $state) {
$compile('<div ui-view="main"><div ui-view="content"></div></div>')($rootScope);

$state.go('main.home');
$q.flush();

expect($state.current.name).toBe('main.home');
}));
});

describe('uiView controllers or onEnter handlers', function() {
var el, template, scope, document, count;

Expand Down

0 comments on commit a94117d

Please sign in to comment.