为什么 ng-bind-html 和 $sanitize 会产生不同的结果?
Why do ng-bind-html and $sanitize produce different results?
我正在尝试清理某些文本区域的内容,我无法使用 ng-bind-html
,因为它会破坏双向绑定(ng-model
不能同时工作)
奇怪的是,当我将 ng-bind-html
应用于模型时,它会产生与我在指令中使用 $sanitize
或 $sce
时不同的结果。
这是我制作的示例
http://plnkr.co/edit/iRvK4med8T9Xqs22BkOe?p=preview
第一个文本区域使用 ng-bind-html
,第二个使用 $sanitize
,第三个应该是 ng-bind-html 指令的代码,因为我从 AngularJS源代码。
"
仅在使用 ng-bind-html 时更改为 "
,在其他两个示例中它更改为 "
如何在我的指令中复制 ng-bind-html
的结果 - 同时保持双向绑定?
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>
结果如我们所料,卫生服务返回相同的结果。在 ngBindHtmlDirective, We can step in and see what is happening. We dive in and examine the values inside the $SanitizeProvider 内放置一个断点。将返回给 ngBindHtmlDirective 的 buf
的值是:
This in "quotes" for testing
与我们调用 $sanitize 得到的结果完全相同,那么真正的区别是什么?真正的区别在于文本框的 innerHTML 和值之间。查看此 example plunker。您可以看到调用两种不同方法的区别,以及转义双引号的不同方式。我没有深入研究 w3 规范或浏览器代码,但我假设 innerHTML 分配在创建 documentFragment、获取它的 textContent、然后将其分配给文本框的值的幕后做了额外的工作。显然 value 只是抓取字符串并按原样插入。
那么你的指令有什么问题?我看到 element.html(processed)
在评论中,但取消评论没有影响。好吧,事实是它确实在一瞬间起作用!尽管使用调试器单步执行,文本框的值已正确设置,但随后 $digest 循环被触发并立即更改它!事实是 ngModelDirective 正在妨碍,特别是 $render function of the baseInputType。我们可以在代码中看到它使用了 element.val
方法。
我们如何在指令中解决这个问题?需要 ngModelController 并覆盖其 $render 函数以使用 element.html
方法代替 (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);
};
我正在尝试清理某些文本区域的内容,我无法使用 ng-bind-html
,因为它会破坏双向绑定(ng-model
不能同时工作)
奇怪的是,当我将 ng-bind-html
应用于模型时,它会产生与我在指令中使用 $sanitize
或 $sce
时不同的结果。
这是我制作的示例
http://plnkr.co/edit/iRvK4med8T9Xqs22BkOe?p=preview
第一个文本区域使用 ng-bind-html
,第二个使用 $sanitize
,第三个应该是 ng-bind-html 指令的代码,因为我从 AngularJS源代码。
"
仅在使用 ng-bind-html 时更改为 "
,在其他两个示例中它更改为 "
如何在我的指令中复制 ng-bind-html
的结果 - 同时保持双向绑定?
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>
结果如我们所料,卫生服务返回相同的结果。在 ngBindHtmlDirective, We can step in and see what is happening. We dive in and examine the values inside the $SanitizeProvider 内放置一个断点。将返回给 ngBindHtmlDirective 的 buf
的值是:
This in "quotes" for testing
与我们调用 $sanitize 得到的结果完全相同,那么真正的区别是什么?真正的区别在于文本框的 innerHTML 和值之间。查看此 example plunker。您可以看到调用两种不同方法的区别,以及转义双引号的不同方式。我没有深入研究 w3 规范或浏览器代码,但我假设 innerHTML 分配在创建 documentFragment、获取它的 textContent、然后将其分配给文本框的值的幕后做了额外的工作。显然 value 只是抓取字符串并按原样插入。
那么你的指令有什么问题?我看到 element.html(processed)
在评论中,但取消评论没有影响。好吧,事实是它确实在一瞬间起作用!尽管使用调试器单步执行,文本框的值已正确设置,但随后 $digest 循环被触发并立即更改它!事实是 ngModelDirective 正在妨碍,特别是 $render function of the baseInputType。我们可以在代码中看到它使用了 element.val
方法。
我们如何在指令中解决这个问题?需要 ngModelController 并覆盖其 $render 函数以使用 element.html
方法代替 (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);
};