From ecac1b5757e28df4ce68dfc2f1172a1ef1130070 Mon Sep 17 00:00:00 2001 From: Jukka Date: Thu, 19 May 2016 22:36:38 +0300 Subject: [PATCH 1/2] fix(ngMessagesInclude): do not compile template if scope is destroyed Messages imported with ngMessagesInclude are loaded asynchronously and they can arrive after the ngMessages element has already been removed from DOM. Previously such messages were still compiled and that caused a $compile:ctreq error. Now they are silently ignored if ngMessagesInclude's scope has already been destroyed. Closes #12695 --- src/ngMessages/messages.js | 2 ++ test/ngMessages/messagesSpec.js | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/ngMessages/messages.js b/src/ngMessages/messages.js index 2ac370f90eec..e1c9b8be4ba4 100644 --- a/src/ngMessages/messages.js +++ b/src/ngMessages/messages.js @@ -550,6 +550,8 @@ angular.module('ngMessages', []) link: function($scope, element, attrs) { var src = attrs.ngMessagesInclude || attrs.src; $templateRequest(src).then(function(html) { + if ($scope.$$destroyed) return; + $compile(html)($scope, function(contents) { element.after(contents); diff --git a/test/ngMessages/messagesSpec.js b/test/ngMessages/messagesSpec.js index 5613844bf591..0530702e2bdd 100644 --- a/test/ngMessages/messagesSpec.js +++ b/test/ngMessages/messagesSpec.js @@ -842,6 +842,33 @@ describe('ngMessages', function() { }) ); + it('should not compile template if scope is destroyed', function() { + module(function($provide) { + $provide.decorator('$compile', ['$delegate', function($delegate) { + var result = jasmine.createSpy('$compile').and.callFake($delegate); + result.$$createComment = $delegate.$$createComment; + return result; + }]); + }); + inject(function($rootScope, $httpBackend, $compile) { + $httpBackend.expectGET('messages.html').respond('
A
'); + $rootScope.show = true; + var html = + '
' + + '
' + + '
' + + '
' + + '
'; + + element = $compile(html)($rootScope); + $rootScope.$digest(); + $rootScope.show = false; + $rootScope.$digest(); + $compile.calls.reset(); + $httpBackend.flush(); + expect($compile).not.toHaveBeenCalled(); + }); + }); }); describe('when multiple', function() { From 422f98a44a6e5d0a36bb7c106461cfd5ba55d7e5 Mon Sep 17 00:00:00 2001 From: Jukka Date: Mon, 23 May 2016 00:35:50 +0300 Subject: [PATCH 2/2] refactor(ngMessagesInclude): change unit test for destroyed scope Use an expectation for not throwing instead of spying on $compile in order to keep the test less implementation specific. --- test/ngMessages/messagesSpec.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/test/ngMessages/messagesSpec.js b/test/ngMessages/messagesSpec.js index 0530702e2bdd..5c56acb8a349 100644 --- a/test/ngMessages/messagesSpec.js +++ b/test/ngMessages/messagesSpec.js @@ -842,14 +842,7 @@ describe('ngMessages', function() { }) ); - it('should not compile template if scope is destroyed', function() { - module(function($provide) { - $provide.decorator('$compile', ['$delegate', function($delegate) { - var result = jasmine.createSpy('$compile').and.callFake($delegate); - result.$$createComment = $delegate.$$createComment; - return result; - }]); - }); + it('should not throw if scope has been destroyed when template request is ready', inject(function($rootScope, $httpBackend, $compile) { $httpBackend.expectGET('messages.html').respond('
A
'); $rootScope.show = true; @@ -864,11 +857,10 @@ describe('ngMessages', function() { $rootScope.$digest(); $rootScope.show = false; $rootScope.$digest(); - $compile.calls.reset(); - $httpBackend.flush(); - expect($compile).not.toHaveBeenCalled(); - }); - }); + expect(function() { + $httpBackend.flush(); + }).not.toThrow(); + })); }); describe('when multiple', function() {