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

10 $digest() iterations reached using $sce inside isolate scope directive 1.2.0-rc.2 #3932

Closed
madaz opened this issue Sep 9, 2013 · 3 comments · Fixed by #4087
Closed

10 $digest() iterations reached using $sce inside isolate scope directive 1.2.0-rc.2 #3932

madaz opened this issue Sep 9, 2013 · 3 comments · Fixed by #4087

Comments

@madaz
Copy link
Contributor

madaz commented Sep 9, 2013

The following code works in 1.2.0-rc1, but is now broken in 1.2.0-rc.2 with the following error

[$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["getSafeHtml(e); newVal: {}; oldVal: {}"],["getSafeHtml(e); newVal: {}; oldVal: {}"],["getSafeHtml(e); newVal: {}; oldVal: {}"],["getSafeHtml(e); newVal: {}; oldVal: {}"],["getSafeHtml(e); newVal: {}; oldVal: {}"]]

Code snippet

angular.module('app', [])
.directive('myError', ['$sce', function ($sce) {

    return {
        restrict: 'E',
        replace: true,
        template:
                '<ul ng-if="errors.length > 0">' +
                   '<li ng-repeat="e in errors" ng-bind-html="getSafeHtml(e)"></li>' +
                '</ul>',
        scope: {
            errors: '='
        },
        link: function (scope, element, attrs) {
            scope.getSafeHtml = function (html) {
                return $sce.trustAsHtml(html);
            };
        }
    };
}])
.controller('AppCtrl', [function () {
    this.someerrors = ['<b>What</b> an error'];
}]);

Html

<div ng-controller="AppCtrl as ctrl" class="container">
  <my-error errors="ctrl.someerrors"></my-error>
</div>
@rodyhaddad
Copy link
Contributor

The problem is that in 1.2.0rc2, .trustAsHtml always returns a new object, and so its return value is never equal to the last time you called it (hence the dirty checking fails, you can't watch it).

A way to get around this is by caching the return value of .trustAsHtml:

var trusted = {};
return {
    restrict: 'E',
    replace: true,
    template:
            '<ul ng-if="errors.length > 0">' +
               '<li ng-repeat="e in errors" ng-bind-html="getSafeHtml(e)"></li>' +
            '</ul>',
    scope: {
        errors: '='
    },
    link: function (scope, element, attrs) {
        scope.getSafeHtml = function (html) {
            return trusted[html] || (trusted[html] = $sce.trustAsHtml(html));
        };
    }
};

plunk

Maybe there's a better way to do this, I've yet to use $sce in my projects.

@chirayuk ?

@madaz
Copy link
Contributor Author

madaz commented Sep 11, 2013

That works. Thanks

@chirayuk
Copy link
Contributor

#4045 (comment)

jamesdaily pushed a commit to jamesdaily/angular.js that referenced this issue Sep 25, 2013
Ref: angular#4045

I have this sinking feeling that support this use case sort of
encourages binding to function that blindly trust some html.  For now,
I'm fixing the issue while I think about the use cases some more.

In the case of a function that performs any non-trivial work before
wrapping the value (e.g. the showdown filter in issue angular#3980, or the
binding to a simply wrapper function in issue angular#3932 if it did anything
meaty), this fix makes it "work" - but performance is going to suck -
you should bind to some other thing on scope that watches the actual
source and adjusts itself when that changes (e.g. the showdown filter.)
For the case of the wrapper in angular#3932, if one isn't performing
sanitization or some such thing - then you the developer has insight
into why that value is safe in that particular context - and it should
be available simply by name and not as a result of a function taking any
arbitrary input to make auditing of security a little saner.

Closes angular#3932, angular#3980
jamesdaily pushed a commit to jamesdaily/angular.js that referenced this issue Jan 27, 2014
Ref: angular#4045

I have this sinking feeling that support this use case sort of
encourages binding to function that blindly trust some html.  For now,
I'm fixing the issue while I think about the use cases some more.

In the case of a function that performs any non-trivial work before
wrapping the value (e.g. the showdown filter in issue angular#3980, or the
binding to a simply wrapper function in issue angular#3932 if it did anything
meaty), this fix makes it "work" - but performance is going to suck -
you should bind to some other thing on scope that watches the actual
source and adjusts itself when that changes (e.g. the showdown filter.)
For the case of the wrapper in angular#3932, if one isn't performing
sanitization or some such thing - then you the developer has insight
into why that value is safe in that particular context - and it should
be available simply by name and not as a result of a function taking any
arbitrary input to make auditing of security a little saner.

Closes angular#3932, angular#3980
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.