diff --git a/docs/content/error/$compile/multilink.ngdoc b/docs/content/error/$compile/multilink.ngdoc new file mode 100644 index 000000000000..6404ec04f69a --- /dev/null +++ b/docs/content/error/$compile/multilink.ngdoc @@ -0,0 +1,27 @@ +@ngdoc error +@name $compile:multilink +@fullName Linking Element Multiple Times +@description + +This error occurs when a single element is linked more then once. + +For example, if an element is compiled and linked twice without cloning: +``` + var linker = $compile(template); + linker($scope); //=> ok + linker($scope); //=> multilink error +``` + +Linking an element as a clone multiple times is ok: +``` + var linker = $compile(template); + linker($scope, function() { ... }); //=> ok + linker($scope, function() { ... }); //=> ok +``` + +However once an element has been linked it can not be re-linked as a clone: +``` + var linker = $compile(template); + linker($scope); //=> ok + linker($scope, function() { ... }); //=> multilink error +``` \ No newline at end of file diff --git a/src/ng/compile.js b/src/ng/compile.js index 47fc44c2e053..6debbaa366e1 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -1817,6 +1817,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { compile.$$addScopeClass($compileNodes); var namespace = null; return function publicLinkFn(scope, cloneConnectFn, options) { + if (!$compileNodes) { + throw $compileMinErr('multilink', 'This element has already been linked.'); + } assertArg(scope, 'scope'); if (previousCompileContext && previousCompileContext.needsNewScope) { @@ -1871,6 +1874,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (cloneConnectFn) cloneConnectFn($linkNode, scope); if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); + + if (!cloneConnectFn) { + $compileNodes = compositeLinkFn = null; + } return $linkNode; }; } diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 4c57925ec5a1..b0998f24dc27 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -3631,6 +3631,14 @@ describe('$compile', function() { expect(element.text()).toBe('3'); }); }); + + it('should throw multilink error when linking the same element more then once', function() { + var linker = $compile('
'); + linker($rootScope).remove(); + expect(function() { + linker($rootScope); + }).toThrowMinErr('$compile', 'multilink', 'This element has already been linked.'); + }); }); @@ -7971,6 +7979,22 @@ describe('$compile', function() { }); }); + it('should throw if a transcluded node is transcluded again', function() { + module(function() { + directive('trans', valueFn({ + transclude: true, + link: function(scope, element, attr, ctrl, $transclude) { + $transclude(); + $transclude(); + } + })); + }); + inject(function($rootScope, $compile) { + expect(function() { + $compile('')($rootScope); + }).toThrowMinErr('$compile', 'multilink', 'This element has already been linked.'); + }); + }); it('should not leak if two "element" transclusions are on the same element (with debug info)', function() { if (jQuery) { @@ -8113,7 +8137,7 @@ describe('$compile', function() { '
' + '' + ''); - element = template($rootScope); + element = template($rootScope, noop); $rootScope.$digest(); $timeout.flush(); $httpBackend.flush(); @@ -8123,10 +8147,11 @@ describe('$compile', function() { $templateCache.removeAll(); var destroyedScope = $rootScope.$new(); destroyedScope.$destroy(); - var clone = template(destroyedScope); + var clone = template(destroyedScope, noop); $rootScope.$digest(); $timeout.flush(); expect(linkFn).not.toHaveBeenCalled(); + clone.remove(); }); });