diff --git a/src/ng/directive/ngBind.js b/src/ng/directive/ngBind.js index de3745747706..64a2d821533b 100644 --- a/src/ng/directive/ngBind.js +++ b/src/ng/directive/ngBind.js @@ -134,11 +134,15 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) { * @element ANY * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. */ -var ngBindHtmlDirective = ['$sce', function($sce) { +var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) { return function(scope, element, attr) { element.addClass('ng-binding').data('$binding', attr.ngBindHtml); - scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) { - element.html($sce.getTrustedHtml(value) || ''); + + var parsed = $parse(attr.ngBindHtml); + function getStringValue() { return (parsed(scope) || '').toString(); } + + scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) { + element.html($sce.getTrustedHtml(parsed(scope)) || ''); }); }; }]; diff --git a/test/ng/directive/ngBindSpec.js b/test/ng/directive/ngBindSpec.js index be68464ffc1a..7af4c13f2b63 100644 --- a/test/ng/directive/ngBindSpec.js +++ b/test/ng/directive/ngBindSpec.js @@ -102,6 +102,18 @@ describe('ngBind*', function() { expect(angular.lowercase(element.html())).toEqual('
hello
'); })); + it('should watch the string value to avoid infinite recursion', inject(function($rootScope, $compile, $sce) { + // Ref: https://github.com/angular/angular.js/issues/3932 + // If the binding is a function that creates a new value on every call via trustAs, we'll + // trigger an infinite digest if we don't take care of it. + element = $compile('
')($rootScope); + $rootScope.getHtml = function() { + return $sce.trustAsHtml('
hello
'); + }; + $rootScope.$digest(); + expect(angular.lowercase(element.html())).toEqual('
hello
'); + })); + describe('when $sanitize is available', function() { beforeEach(function() { module('ngSanitize'); });