I'm trying to sanitize the content of some text areas, I cannot use ng-bind-html
because it breaks two way binding (ng-model
does not work at the same time)
Strangely when I apply ng-bind-html
to a model it produces a different result to when I use $sanitize
or $sce
inside of a directive.
Here's a sample I made up
http://plnkr.co/edit/iRvK4med8T9Xqs22BkOe?p=preview
First text area uses ng-bind-html
, the second uses $sanitize
and the third should be the code for the ng-bind-html directive as I ripped out of the AngularJS source code.
"
is only corrected changed to "
when using ng-bind-html, in the other two examples it changes to "
How can I replicate the results of ng-bind-html
in my directive - while keeping the two way binding?
angular.module('sanitizeExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', '$sce',
function($scope, $sce) {
$scope.value = 'This in "quotes" for testing';
$scope.model = 'This in "quotes" for testing';
}
]).directive('sanitize', ['$sanitize', '$parse', '$sce',
function($sanitize, $parse, $sce) {
return {
restrict: 'A',
replace: true,
scope: true,
link: function(scope, element, attrs) {
var process = function(input) {
return $sanitize(input);
//return $sce.getTrustedHtml(input);
};
var processed = process(scope.model);
console.log(processed); // Output here = This in "quotes" for testing
$parse(attrs.ngModel).assign(scope, processed);
//element.html(processed);
}
};
}
])
.directive('sanitizeBindHtml', ['$parse', '$sce',
function($parse, $sce) {
return {
restrict: 'A',
replace: true,
scope: true,
link: function(scope, element, attrs) {
var parsed = $parse(attrs.ngModel);
function getStringValue() {
var value = parsed(scope);
getStringValue.$$unwatch = parsed.$$unwatch;
return (value || '').toString();
}
scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) {
var processed = $sce.getTrustedHtml(parsed(scope)) || '';
$parse(attrs.ngModel).assign(scope, processed)
});
}
};
}
]);
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular-sanitize.js"></script>
<!doctype html>
<html lang="en">
<body ng-app="sanitizeExample">
<div ng-controller="ExampleController">
<textarea ng-bind-html="value"></textarea>
<br/>{{value}}
<br/>
<br/>
<textarea sanitize ng-model="model"></textarea>
<br/>
<br/>
<textarea sanitize-bind-html ng-model="model"></textarea>
</div>
</body>
It turns out like we would expect, the sanitation service is returning the same result. Placing a breakpoint inside the ngBindHtmlDirective, We can step in and see what is happening. We dive in and examine the values inside the $SanitizeProvider. The value of buf
that will be returned back to the ngBindHtmlDirective is:
This in "quotes" for testing
The exact same as we get for calling $sanitize, so what's the real difference? The real difference is between a textbox's innerHTML and value. View this example plunker. You can see the difference between calling the two different methods, with the different ways of escaping a double quote. I didn't go digging though the w3 spec or the browser code, but I assume the innerHTML assignment is doing additional work under the hood of creating a documentFragment, grabbing it's textContent, then assigning that to the textbox's value. Obviously value is just grabbing the string and inserting it as is.
So what's the problem with your directives? I see that element.html(processed)
is in a comment, but uncommenting it doesn't have an affect. Well the truth is that it does work for a split second! Stepping though with the debugger, the value of the textbox is correctly set, but then a $digest cycle gets fired and immediate changes it! The truth is the ngModelDirective is getting in the way, specifically it's the $render function of the baseInputType. We can see in the code it is using the element.val
method.
How can we fix this in the directives? Require the ngModelController and override its $render function to use element.html
method instead (example plunker).
// Add require to get the controller
require: 'ngModel',
// Controller is passed in as the 4th argument
link: function(scope, element, attrs, ngModelCtrl) {
// modify the $render function to process it as HTML
ngModelCtrl.$render = function() {
element.html(ngModelCtrl.$isEmpty(ngModelCtrl.$viewValue) ? '' : ngModelCtrl.$viewValue);
};
来源:https://stackoverflow.com/questions/31656190/why-do-ng-bind-html-and-sanitize-produce-different-results