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

Commit

Permalink
fix(ngIf): ngIf removes elements dynamically added to it
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
btford committed Oct 30, 2013
1 parent 9d0a697 commit e19067c
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
35 changes: 27 additions & 8 deletions src/ng/directive/ngIf.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,44 @@ 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);
});
}
});
};
}
};

// 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);
}
}];
37 changes: 37 additions & 0 deletions test/ng/directive/ngIfSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down

0 comments on commit e19067c

Please sign in to comment.