Skip to content

Commit

Permalink
fix(ngSwitch): not leak when transcluded and cloned
Browse files Browse the repository at this point in the history
The leak can occur when ngSwich is used inside ngRepeat or any other
directive which does not attached transcluded content to DOM but clones
it.

Refactor ngSwitch to use controller instead of storing data on compile
node.

Closes angular#1621
  • Loading branch information
danilsomsikov committed Jan 11, 2013
1 parent 7dff7bb commit cced1ae
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 32 deletions.
65 changes: 33 additions & 32 deletions src/ng/directive/ngSwitch.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,51 +62,52 @@
var NG_SWITCH = 'ng-switch';
var ngSwitchDirective = valueFn({
restrict: 'EA',
compile: function(element, attr) {
require: 'ngSwitch',
controller: function ngSwitchController() {
this.cases = {};
},
link: function(scope, element, attr, ctrl) {
var watchExpr = attr.ngSwitch || attr.on,
cases = {};
selectedTransclude,
selectedElement,
selectedScope;

element.data(NG_SWITCH, cases);
return function(scope, element){
var selectedTransclude,
selectedElement,
selectedScope;

scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
if (selectedElement) {
selectedScope.$destroy();
selectedElement.remove();
selectedElement = selectedScope = null;
}
if ((selectedTransclude = cases['!' + value] || cases['?'])) {
scope.$eval(attr.change);
selectedScope = scope.$new();
selectedTransclude(selectedScope, function(caseElement) {
selectedElement = caseElement;
element.append(caseElement);
});
}
});
};
scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
if (selectedElement) {
selectedScope.$destroy();
selectedElement.remove();
selectedElement = selectedScope = null;
}
if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
scope.$eval(attr.change);
selectedScope = scope.$new();
selectedTransclude(selectedScope, function(caseElement) {
selectedElement = caseElement;
element.append(caseElement);
});
}
});
}
});

var ngSwitchWhenDirective = ngDirective({
transclude: 'element',
priority: 500,
compile: function(element, attrs, transclude) {
var cases = element.inheritedData(NG_SWITCH);
assertArg(cases);
cases['!' + attrs.ngSwitchWhen] = transclude;
require: '^ngSwitch',
compile: function(element, attrs, transclude, ctrl) {
return function(scope, element, attr, ctrl) {
ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
};
}
});

var ngSwitchDefaultDirective = ngDirective({
transclude: 'element',
priority: 500,
compile: function(element, attrs, transclude) {
var cases = element.inheritedData(NG_SWITCH);
assertArg(cases);
cases['?'] = transclude;
require: '^ngSwitch',
compile: function(element, attrs, transclude, ctrl) {
return function(scope, element, attr, ctrl) {
ctrl.cases['?'] = transclude;
};
}
});
12 changes: 12 additions & 0 deletions test/ng/directive/ngSwitchSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,16 @@ describe('ngSwitch', function() {
expect(child2).toBeDefined();
expect(child2).not.toBe(child1);
}));


it('should not leak when comiled node is not attached to DOM but cloned',
inject(function($rootScope, $compile) {
element = $compile(
'<div ng-repeat="i in []">' +
'<ng-switch on="url">' +
'<div ng-switch-when="a">{{name}}</div>' +
'</ng-switch>' +
'</div>')($rootScope);
$rootScope.$apply();
}));
});

0 comments on commit cced1ae

Please sign in to comment.