Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(ngView): accidentally compiling leaving content #2562

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,10 @@ function createInjector(modulesToLoad) {
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: annotate
annotate: annotate,
has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
}
};
}
}
10 changes: 3 additions & 7 deletions src/ng/animation.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,9 @@ function $AnimationProvider($provide) {
*/
return function $animation(name) {
if (name) {
try {
return $injector.get(camelCase(name) + suffix);
} catch (e) {
//TODO(misko): this is a hack! we should have a better way to test if the injector has a given key.
// The issue is that the animations are optional, and if not present they should be silently ignored.
// The proper way to fix this is to add API onto the injector so that we can ask to see if a given
// animation is supported.
var animationName = camelCase(name) + suffix;
if ($injector.has(animationName)) {
return $injector.get(animationName);
}
}
}
Expand Down
89 changes: 44 additions & 45 deletions src/ng/animator.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,6 @@ var $AnimatorProvider = function() {
* @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
*/
var AnimatorService = function(scope, attrs) {
var ngAnimateAttr = attrs.ngAnimate;
var ngAnimateValue = ngAnimateAttr && scope.$eval(ngAnimateAttr);
var animator = {};

/**
Expand Down Expand Up @@ -223,24 +221,22 @@ var $AnimatorProvider = function() {
return animator;

function animateActionFactory(type, beforeFn, afterFn) {
var className = ngAnimateAttr
? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
: '';
var animationPolyfill = $animation(className);

var polyfillSetup = animationPolyfill && animationPolyfill.setup;
var polyfillStart = animationPolyfill && animationPolyfill.start;

if (!className) {
return function(element, parent, after) {
return function(element, parent, after) {
var ngAnimateValue = scope.$eval(attrs.ngAnimate);
var className = ngAnimateValue
? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
: '';
var animationPolyfill = $animation(className);
var polyfillSetup = animationPolyfill && animationPolyfill.setup;
var polyfillStart = animationPolyfill && animationPolyfill.start;

if (!className) {
beforeFn(element, parent, after);
afterFn(element, parent, after);
}
} else {
var setupClass = className + '-setup';
var startClass = className + '-start';

return function(element, parent, after) {
} else {
var setupClass = className + '-setup';
var startClass = className + '-start';

if (!parent) {
parent = after ? after.parent() : element.parent();
}
Expand All @@ -255,45 +251,48 @@ var $AnimatorProvider = function() {
element.addClass(setupClass);
beforeFn(element, parent, after);
if (element.length == 0) return done();

var memento = (polyfillSetup || noop)(element);

// $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
// keep at 1 for animation dom rerender
$window.setTimeout(beginAnimation, 1);

function beginAnimation() {
element.addClass(startClass);
if (polyfillStart) {
polyfillStart(element, done, memento);
} else if (isFunction($window.getComputedStyle)) {
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
var w3cTransitionProp = 'transition'; //one day all browsers will have this

var durationKey = 'Duration';
var duration = 0;
//we want all the styles defined before and after
forEach(element, function(element) {
};

function beginAnimation() {
element.addClass(startClass);
if (polyfillStart) {
polyfillStart(element, done, memento);
} else if (isFunction($window.getComputedStyle)) {
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
var w3cTransitionProp = 'transition'; //one day all browsers will have this

var durationKey = 'Duration';
var duration = 0;

//we want all the styles defined before and after
forEach(element, function(element) {
if (element.nodeType == 1) {
var globalStyles = $window.getComputedStyle(element) || {};
duration = Math.max(
parseFloat(globalStyles[w3cTransitionProp + durationKey]) ||
parseFloat(globalStyles[vendorTransitionProp + durationKey]) ||
0,
duration);
});
$window.setTimeout(done, duration * 1000);
} else {
done();
}
}

function done() {
afterFn(element, parent, after);
element.removeClass(setupClass);
element.removeClass(startClass);
element.removeData(NG_ANIMATE_CONTROLLER);
}
});
$window.setTimeout(done, duration * 1000);
} else {
done();
}
}

function done() {
afterFn(element, parent, after);
element.removeClass(setupClass);
element.removeClass(startClass);
element.removeData(NG_ANIMATE_CONTROLLER);
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/ng/directive/ngView.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,10 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c

if (template) {
clearContent();
animate.enter(jqLite('<div></div>').html(template).contents(), element);
var enterElements = jqLite('<div></div>').html(template).contents();
animate.enter(enterElements, element);

var link = $compile(element.contents()),
var link = $compile(enterElements),
current = $route.current,
controller;

Expand Down
12 changes: 7 additions & 5 deletions src/ng/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,14 @@ function $LocationProvider(){
rewrittenUrl = $location.$$rewrite(absHref);

if (absHref && !elm.attr('target') && rewrittenUrl) {
// update location manually
$location.$$parse(rewrittenUrl);
$rootScope.$apply();
event.preventDefault();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true;
if (rewrittenUrl != initialUrl) {
// update location manually
$location.$$parse(rewrittenUrl);
$rootScope.$apply();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true;
}
}
});

Expand Down
9 changes: 9 additions & 0 deletions test/auto/injectorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ describe('injector', function() {
});


it('should allow query names', function() {
providers('abc', function () { return ''; });

expect(injector.has('abc')).toBe(true);
expect(injector.has('xyz')).toBe(false);
expect(injector.has('$injector')).toBe(true);
});


it('should provide useful message if no provider', function() {
expect(function() {
injector.get('idontexist');
Expand Down
25 changes: 22 additions & 3 deletions test/ng/animatorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,29 @@ describe("$animator", function() {
}));
});

it("should throw an error when an invalid ng-animate syntax is provided", inject(function($compile, $rootScope) {
describe('anmation evaluation', function () {
it('should re-evaluate the animation expression on each animation', inject(function($animator, $rootScope) {
var parent = jqLite('<div><span></span></div>');
var element = parent.find('span');

$rootScope.animationFn = function () { throw new Error('too early'); };
var animate = $animator($rootScope, { ngAnimate: 'animationFn()' });
var log = '';

$rootScope.animationFn = function () { log = 'abc' };
animate.enter(element, parent);
expect(log).toEqual('abc');

$rootScope.animationFn = function () { log = 'xyz' };
animate.enter(element, parent);
expect(log).toEqual('xyz');
}));
});

it("should throw an error when an invalid ng-animate syntax is provided", inject(function($animator, $rootScope) {
expect(function() {
element = $compile('<div ng-repeat="i in is" ng-animate=":"></div>')($rootScope);
$rootScope.$digest();
var animate = $animator($rootScope, { ngAnimate: ':' });
animate.enter();
}).toThrow("Syntax Error: Token ':' not a primary expression at column 1 of the expression [:] starting at [:].");
}));
});
46 changes: 44 additions & 2 deletions test/ng/directive/ngViewSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
describe('ngView', function() {
var element;

beforeEach(module(function() {
beforeEach(module(function($provide) {
$provide.value('$window', angular.mock.createMockWindow());
return function($rootScope, $compile, $animator) {
element = $compile('<ng:view onload="load()"></ng:view>')($rootScope);
$animator.enabled(true);
Expand Down Expand Up @@ -621,5 +622,46 @@ describe('ngView', function() {
}
}));


it('should not double compile when route changes', function() {
module(function($routeProvider, $animationProvider, $provide) {
$routeProvider.when('/foo', {template: '<div ng-repeat="i in [1,2]">{{i}}</div>'});
$routeProvider.when('/bar', {template: '<div ng-repeat="i in [3,4]">{{i}}</div>'});
$animationProvider.register('my-animation-leave', function() {
return {
start: function(element, done) {
done();
}
};
});
});

inject(function($rootScope, $compile, $location, $route, $window, $rootElement, $sniffer) {
element = $compile(html('<ng:view onload="load()" ng-animate="\'my-animation\'"></ng:view>'))($rootScope);

$location.path('/foo');
$rootScope.$digest();
if ($sniffer.supportsTransitions) {
$window.setTimeout.expect(1).process();
$window.setTimeout.expect(0).process();
}
expect(element.text()).toEqual('12');

$location.path('/bar');
$rootScope.$digest();
expect(n(element.text())).toEqual('1234');
if ($sniffer.supportsTransitions) {
$window.setTimeout.expect(1).process();
$window.setTimeout.expect(1).process();
} else {
$window.setTimeout.expect(1).process();
}
expect(element.text()).toEqual('34');

function n(text) {
return text.replace(/\r\n/m, '').replace(/\r\n/m, '');
}
});
});
});
});
});
13 changes: 13 additions & 0 deletions test/ng/locationSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,19 @@ describe('$location', function() {
});


it('should do nothing if already on the same URL', function() {
configureService('/base/', true, true);
inject(
initBrowser(),
initLocation(),
function($browser) {
browserTrigger(link, 'click');
expectNoRewrite($browser, 'http://host.com/base/');
}
);
});


it('should rewrite abs link to new url when history enabled on new browser', function() {
configureService('/base/link?a#b', true, true);
inject(
Expand Down