aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Ford2013-10-30 14:51:02 -0700
committerBrian Ford2013-10-30 16:21:02 -0700
commite19067c9bbac3c3bb450c80f73eb5518bd0db1a1 (patch)
treee4e0220a67c2d0c57077ecdd4eedfd85e81c2b3a
parent9d0a69772c39bfc751ca2000c3b4b3381e51fe93 (diff)
downloadangular.js-e19067c9bbac3c3bb450c80f73eb5518bd0db1a1.tar.bz2
fix(ngIf): ngIf removes elements dynamically added to it
When using ngIf with ngInclude on the same element, ngIf previously did not remove elements added by ngInclude. Similarly, when using ngIfStart/End, ngIf will miss elements added between the start/end markers added after ngIf is linked. This commit changes the behavior of ngIf to add a comment node at the end of its elements such that elements between the starting comment and this ending comment are removed when ngIf's predicate does not hold.
-rw-r--r--src/ng/compile.js2
-rw-r--r--src/ng/directive/ngIf.js35
-rwxr-xr-xtest/ng/directive/ngIfSpec.js37
3 files changed, 65 insertions, 9 deletions
diff --git a/src/ng/compile.js b/src/ng/compile.js
index 7754a8e6..af7d5e6b 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -1175,7 +1175,7 @@ function $CompileProvider($provide) {
if (directiveValue = directive.transclude) {
// Special case ngRepeat so that we don't complain about duplicate transclusion, ngRepeat
// knows how to handle this on its own.
- if (directiveName !== 'ngRepeat') {
+ if (directiveName !== 'ngRepeat' && directiveName !== 'ngIf') {
assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode);
transcludeDirective = directive;
}
diff --git a/src/ng/directive/ngIf.js b/src/ng/directive/ngIf.js
index b2811ab4..662f3f32 100644
--- a/src/ng/directive/ngIf.js
+++ b/src/ng/directive/ngIf.js
@@ -86,20 +86,21 @@ var ngIfDirective = ['$animate', function($animate) {
restrict: 'A',
compile: function (element, attr, transclude) {
return function ($scope, $element, $attr) {
- var childElement, childScope;
+ var block = {}, childScope;
$scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
- if (childElement) {
- $animate.leave(childElement);
- childElement = undefined;
+ if (block.startNode) {
+ $animate.leave(getBlockElements(block));
+ block = {};
}
- if (childScope) {
- childScope.$destroy();
- childScope = undefined;
+ if (block.startNode) {
+ getBlockElements(block).$destroy();
+ block = {};
}
if (toBoolean(value)) {
childScope = $scope.$new();
transclude(childScope, function (clone) {
- childElement = clone;
+ block.startNode = clone[0];
+ block.endNode = clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
$animate.enter(clone, $element.parent(), $element);
});
}
@@ -107,4 +108,22 @@ var ngIfDirective = ['$animate', function($animate) {
};
}
};
+
+ // TODO(bford): this helper was copypasta'd from ngRepeat
+ function getBlockElements(block) {
+ if (block.startNode === block.endNode) {
+ return jqLite(block.startNode);
+ }
+
+ var element = block.startNode;
+ var elements = [element];
+
+ do {
+ element = element.nextSibling;
+ if (!element) break;
+ elements.push(element);
+ } while (element !== block.endNode);
+
+ return jqLite(elements);
+ }
}];
diff --git a/test/ng/directive/ngIfSpec.js b/test/ng/directive/ngIfSpec.js
index 79eab6bb..509cf26d 100755
--- a/test/ng/directive/ngIfSpec.js
+++ b/test/ng/directive/ngIfSpec.js
@@ -60,6 +60,43 @@ describe('ngIf', function () {
expect(element.children().length).toBe(9);
});
+ it('should play nice with ngInclude on the same element', inject(function($templateCache) {
+ $templateCache.put('test.html', [200, '{{value}}', {}]);
+
+ $scope.value = 'first';
+ element.append($compile(
+ '<div ng-if="value==\'first\'" ng-include="\'test.html\'"></div>'
+ )($scope));
+ $scope.$apply();
+ expect(element.text()).toBe('first');
+
+ $scope.value = 'later';
+ $scope.$apply();
+ expect(element.text()).toBe('');
+ }));
+
+ it('should work with multiple elements', function() {
+ $scope.show = true;
+ $scope.things = [1, 2, 3];
+ element.append($compile(
+ '<div>before;</div>' +
+ '<div ng-if-start="show">start;</div>' +
+ '<div ng-repeat="thing in things">{{thing}};</div>' +
+ '<div ng-if-end>end;</div>' +
+ '<div>after;</div>'
+ )($scope));
+ $scope.$apply();
+ expect(element.text()).toBe('before;start;1;2;3;end;after;');
+
+ $scope.things.push(4);
+ $scope.$apply();
+ expect(element.text()).toBe('before;start;1;2;3;4;end;after;');
+
+ $scope.show = false;
+ $scope.$apply();
+ expect(element.text()).toBe('before;after;');
+ });
+
it('should restore the element to its compiled state', function() {
$scope.value = true;
makeIf('value');