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

Commit 2fe5773

Browse files
committed
fix($compile): disallow linking the same element more then once
Previously the following would invoke the elemnet link function multiple times, causing unknown and potentially buggy results: var link = $compile(html); link(scope); link(scope); This was always unsupported. Now this throws a multilink error.
1 parent 53a3bf6 commit 2fe5773

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@ngdoc error
2+
@name $compile:multilink
3+
@fullName Linking Element Multiple Times
4+
@description
5+
6+
This error occurs when a single element is linked more then once.
7+
8+
For example, if an element is compiled and linked twice without cloning:
9+
```
10+
var linker = $compile(template);
11+
linker($scope); //=> ok
12+
linker($scope); //=> multilink error
13+
```
14+
15+
Linking an element as a clone multiple times is ok:
16+
```
17+
var linker = $compile(template);
18+
linker($scope, function() { ... }); //=> ok
19+
linker($scope, function() { ... }); //=> ok
20+
```
21+
22+
However once an element has been linked it can not be re-linked as a clone:
23+
```
24+
var linker = $compile(template);
25+
linker($scope); //=> ok
26+
linker($scope, function() { ... }); //=> multilink error
27+
```

src/ng/compile.js

+7
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18171817
compile.$$addScopeClass($compileNodes);
18181818
var namespace = null;
18191819
return function publicLinkFn(scope, cloneConnectFn, options) {
1820+
if (!$compileNodes) {
1821+
throw $compileMinErr('multilink', 'This element has already been linked.');
1822+
}
18201823
assertArg(scope, 'scope');
18211824

18221825
if (previousCompileContext && previousCompileContext.needsNewScope) {
@@ -1871,6 +1874,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
18711874

18721875
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
18731876
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
1877+
1878+
if (!cloneConnectFn) {
1879+
$compileNodes = compositeLinkFn = null;
1880+
}
18741881
return $linkNode;
18751882
};
18761883
}

test/ng/compileSpec.js

+27-2
Original file line numberDiff line numberDiff line change
@@ -3631,6 +3631,14 @@ describe('$compile', function() {
36313631
expect(element.text()).toBe('3');
36323632
});
36333633
});
3634+
3635+
it('should throw multilink error when linking the same element more then once', function() {
3636+
var linker = $compile('<div>');
3637+
linker($rootScope).remove();
3638+
expect(function() {
3639+
linker($rootScope);
3640+
}).toThrowMinErr('$compile', 'multilink', 'This element has already been linked.');
3641+
});
36343642
});
36353643

36363644

@@ -7971,6 +7979,22 @@ describe('$compile', function() {
79717979
});
79727980
});
79737981

7982+
it('should throw if a transcluded node is transcluded again', function() {
7983+
module(function() {
7984+
directive('trans', valueFn({
7985+
transclude: true,
7986+
link: function(scope, element, attr, ctrl, $transclude) {
7987+
$transclude();
7988+
$transclude();
7989+
}
7990+
}));
7991+
});
7992+
inject(function($rootScope, $compile) {
7993+
expect(function() {
7994+
$compile('<trans></trans>')($rootScope);
7995+
}).toThrowMinErr('$compile', 'multilink', 'This element has already been linked.');
7996+
});
7997+
});
79747998

79757999
it('should not leak if two "element" transclusions are on the same element (with debug info)', function() {
79768000
if (jQuery) {
@@ -8113,7 +8137,7 @@ describe('$compile', function() {
81138137
'</div>' +
81148138
'</div>' +
81158139
'</div>');
8116-
element = template($rootScope);
8140+
element = template($rootScope, noop);
81178141
$rootScope.$digest();
81188142
$timeout.flush();
81198143
$httpBackend.flush();
@@ -8123,10 +8147,11 @@ describe('$compile', function() {
81238147
$templateCache.removeAll();
81248148
var destroyedScope = $rootScope.$new();
81258149
destroyedScope.$destroy();
8126-
var clone = template(destroyedScope);
8150+
var clone = template(destroyedScope, noop);
81278151
$rootScope.$digest();
81288152
$timeout.flush();
81298153
expect(linkFn).not.toHaveBeenCalled();
8154+
clone.remove();
81308155
});
81318156
});
81328157

0 commit comments

Comments
 (0)