为什么 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 &#34;quotes&#34; 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);
};