通过$ sce.trustAsHtml添加字符串中Angular js中的绑定数据

时间:2014-08-05 00:43:46

标签: angularjs binding

我正在为遗留系统实现Web界面,因此来自服务器的数据是固定的。该服务器数据指定要向用户显示的各种控件,例如,组合框,按钮等我已经解析了服务器数据,并通过$ sce.trustAsHtml()为控件添加了HTML。

问题是控件未绑定到模型。如果我输入ng-change事件处理程序,则不会使用用户编辑调用它。

我读了这篇有用的帖子:call function inside $sce.trustAsHtml() string in Angular js表示:

  

ng-bind-html只会插入普通的旧HTML和   不打扰编译它(所以任何指令   html不会被angular处理。

所以这似乎是我的问题。但我不太了解Angular,以便理解上述帖子提供的解决方案。

我在以下HTML中显示动态文本

<span ng-repeat="i in range(0, item.Data.DisplayText.JSArray.length-1)">
  <span ng-bind-html="item.Data.DisplayText.JSArray[i] | trusted_html"></span>
  <span ng-show="!$last"><br></span>
</span>

以下是range()的定义:

$scope.range = function(min, max){
  var input = [];
  for (var i=min; i<=max; i++) input.push(i);
  return input;
};

以下是过滤器的定义:

app.filter('trusted_html', ['$sce', function($sce){
    return function(text) {
        return $sce.trustAsHtml(text);
    };
}]);

我已经帮助设置了部分内容,并在堆栈溢出时从这里收集了解决方案。我很抱歉无法提供这些原始解决方案的链接。

我认为我需要做的是扩展我的trusted_html过滤器,以便$ $编译文本。但我不知道是否会编译整个DOM(可能很慢)或只是文本参数。

任何帮助将不胜感激。


附录:

我很欣赏@Simeon Cheeseman的回应。他表示,如我最初想要的那样,指令会比过滤器更好。我一直在研究文档和阅读帖子,看起来他是100%正确的。 DOM操作应该在指令中进行。但是,我不喜欢有一个指令(例如上面链接中的示例中的'compile-template')修复另一个指令(ng-bind-html)的缺点。这对我来说似乎很糟糕。我如何知道ng-bind-html指令将在compile-template指令之前执行?如果执行顺序被颠倒会发生什么?

所以我将考虑是否可以将它们组合成一个函数。我会在下面大声思考。如果有人发现问题,请告诉我。

首先,我查看了angular.js中的ng-bind-html指令:https://code.angularjs.org/1.2.9/angular.js,在此文件中,我搜索了'ngBindHTML'。

var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) {
  return function(scope, element, attr) {
    element.addClass('ng-binding').data('$binding', attr.ngBindHtml);

    var parsed = $parse(attr.ngBindHtml);
    function getStringValue() { return (parsed(scope) || '').toString(); }

    scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) {
      element.html($sce.getTrustedHtml(parsed(scope)) || '');
    });
 };

}];

以上不是app.directive()的形式,也不会返回{link:fun()}对象。所以我不确定这是否直接适用于与下面的compile-template指令进行比较。但是,我确实在2025行找到了以下内容。所以我认为我走的是正确的道路:

ngBindHtml: ngBindHtmlDirective,

所以这个ngBindHtmlDirective以某种方式充当ngBindHtml。

我将挑选上面的代码,并对其进行改革。而且,仅供参考,我对javascript相对较新,匿名函数和闭包对我来说仍然有点新鲜。所以我试图删除匿名函数以便我清楚。我将添加一些评论,这些评论来自我在Angular的网站上提到的信息等。

var DirectiveFnFactory = function($sce, $parse) {

  var DirectiveHandlerFn = function(scope, element, attr) {
    //Input:  scope = an Angular scope object 
    //        element = the jqLite-wrapped element that this directive matches
    //        attr = a hash object with key-value pairs of normalized attribute 
    //               names and their corresponding attribute values
    //closure scope input: $compile object
    //                     $parse object
    //Result: none

    element.addClass('ng-binding');
    // .addClass is jQuery:  http://api.jquery.com/addclass/
    //  adds class to element
    element.data('$binding', attr.ngBindHtml);
    //  adds value to key of '$binding';

    var parsed = $parse(attr.ngBindHtml); 
    //Input:  attr.ngBindHtml should be the HTML to be bound.
    //Result:  $parse() returns a function which represents the 
    //         compiled input expression.
    //  This function will have this signature: 
    //     function(context, locals)    
    //            context – {object} – an object against which any expressions 
    //                                 embedded in the strings are evaluated against 
    //                                 (typically a scope object).
    //            locals – {object=} – local variables context object, useful for 
    //                                 overriding values in context.
    //     The returned function also has the following properties:
    //       literal – {boolean} – whether the expression's top-level node is a 
    //                             JavaScript literal.
    //                           constant – {boolean} – whether the expression is made 
    //                             entirely of JavaScript constant literals.
    //        assign – {?function(context, value)} – if the expression is assignable, 
    //                             this will be set to a function to change its value 
    //                             on the given context.

    function getStringValue() { return (parsed(scope) || '').toString(); }
    //First, this is executing the function that was returned by parsed(), 
    //    passing scope that was given to DirectiveHandlerFn()
    //Next converting Fn() output to string. 
    //In the case of binding HTML. I would think that the output of this would be HTML

    var watchListener = function ngBindHtmlWatchAction(value) {
      element.html($sce.getTrustedHtml(parsed(scope)) || '');
      //.html is jquery: http://api.jquery.com/html/
      //  Get the HTML contents of the first element in the set of matched elements
      //    or set the HTML contents of every matched element.
    }

    scope.$watch(getStringValue, watchListener);
    //$watch signature: 
    //  $watch(watchExpression, listener, [objectEquality]);
    //  This registers a listener callback to be executed whenever the 
    //    watchExpression()  changes
    //  The listener() has this signature (determined by example, not API documents!):
    //     listener(newValue, oldValue);
    //  The listener is called automagically by Angular when the value changes.
  }

  return DirectiveHandlerFn;
}

var ngBindHtmlDirective = ['$sce', '$parse', DirectiveFnFactory];

好的,现在我将首先引用compileTemplate指令,(从这里:call function inside $sce.trustAsHtml() string in Angular js):

.directive('compileTemplate', function($compile, $parse){
    return {
        link: function(scope, element, attar){
            var parsed = $parse(attr.ngBindHtml);
            function getStringValue() { return (parsed(scope) || '').toString(); }

            //Recompile if the template changes
            scope.$watch(getStringValue, function() {
                $compile(element, null, -9999)(scope);  
               //The -9999 makes it skip directives so that we 
               //do not recompile ourselves
            });
        }         
    }
});

然后选择它以允许评论:

var DirectiveObjFactory = function($compile, $parse){
    //input: $compile object
    //       $parse object

    var DirectiveHandlerFn = function(scope, element, attr) {
        //Input:  scope = an Angular scope object 
        //        element = the jqLite-wrapped element that this directive matches
        //        attr = a hash object with key-value pairs of normalized attribute 
        //               names and their corresponding attribute values
        //closure scope vars: $compile object
        //                    $parse object
        //Result: none

        var parsed = $parse(attr.ngBindHtml);
        //Input:  attr.ngBindHtml should be the HTML to be bound.
        //Result:  $parse() returns a function which represents the 
        //         compiled input expression.
        //  This resulted function will have this signature: 
        //     function(context, locals)    
        //            context – {object} – an object against which any expressions 
        //                             embedded in the strings are evaluated against 
        //                             (typically a scope object).
        //            locals – {object=} – local variables context object, useful for 
        //                             overriding values in context.
        //     The returned function also has the following properties:
        //       literal – {boolean} – whether the expression's top-level node is a 
        //                         JavaScript literal.
        //       constant – {boolean} – whether the expression is made 
        //                             entirely of JavaScript constant literals.
        //        assign – {?function(context, value)} – if the expr is assignable, 
        //                          this will be set to a function to change its value 
        //                             on the given context.

       function getStringValue() { return (parsed(scope) || '').toString(); }
        //First, this is executing the function that was returned by parsed(), 
        //    passing scope that was given to DirectiveHandlerFn()
        //Next converting Fn() output to string. 
        //In the case of binding HTML. I would think that the output of this 
        //  would be HTML

        var watchListener = function ngBindHtmlWatchAction(value) {
          //Input: value -- actual the newValue. (oldValue not accepted here)
          //Locally scoped vars used -- element, scope

          // -- Adding Below is from ngbindHtml ------------
          element.html($sce.getTrustedHtml(parsed(scope)) || '');
          //.html is jquery: http://api.jquery.com/html/
          //Gets the HTML contents of the first element in the set of matched 
          //    elements or set the HTML contents of every matched element.
          // -- End addition  ------------

          var compFn = $compile(element, null, -9999);
          //NOTE: I can't find formal documentation for the parameters for $compile()
          //      below is from examples found...
          //Input: element -- the HTML element to compile
          //       transcludeFunction -- null here.
          //       maxPriority -- "The -9999 makes it skip directives so that we 
          //                       do not recompile ourselves"
          //$compile() compiles an HTML string or DOM into a template and 
          //    produces a template function, which can then be used to link scope 
          //    and the template together.
          //  The returned function accepts a scope variable, against which the code
          //    is evaluated. 
          compFn(scope); // execute the returned function, passing scope  
        } // end watchListener

        scope.$watch(getStringValue, watchListener);
        //$watch() function signature: 
        //  $watch(watchExpression, listener, [objectEquality]);
        //  This registers a listener callback to be executed whenever the 
        //    watchExpression changes
        //  The supplied listener() should have this signature:
        //     listener(newValue, oldValue);
        //  The listener is called automagically by Angular when the value changes.

    } // end DirectiveHandlerFn

    return {link: DirectiveHandlerFn}         

} // end DirectiveObjFactory

app.directive('compileTemplate', DirectiveObjFactory);

我想我差不多了。让我试着把这一切都重新组合起来......

.directive('bindAndWatchHtml', ['$sce', function($compile, $parse){
    return {
        link: function(scope, element, attr){
            var parsed = $parse(attr.ngBindHtml);
            function getStringValue() { return (parsed(scope) || '').toString(); }

            //Recompile if the template changes
            scope.$watch(getStringValue, function() {
                element.html($sce.getTrustedHtml(parsed(scope)) || '');
                $compile(element, null, -9999)(scope);  
                //The -9999 makes it skip directives so that we do not recompile ourselves
            });
        }         
    }
}]);

希望这将绑定并编译html,并同时信任HTML。

现在进行测试......

附录:

不起作用..在线:

element.html($sce.getTrustedHtml(parsed(scope)) || '');

它抱怨$ sce没有定义。

...

我更改了以下行,并允许定义$ sce。

app.directive('bindAndWatchTrustedHtml0', ['$compile', '$parse', '$sce',
                                       function($compile, $parse, $sce){ ...

接下来,我在尝试在不安全的位置使用安全文本时收到错误....

...

这只是花了太长时间。我放弃了这个。我使用了这个,根据最顶层的原始链接,它可以工作。

<span ng-bind-html="item.Data.DisplayText.JSArray[i] | trusted_html" compile-template>
</span>

1 个答案:

答案 0 :(得分:1)

请参阅https://code.angularjs.org/1.2.19/docs/api/ng/service/ $ compile以了解如何编译。

基本上你打电话给compiledHtml = $compile('<div>' + text + '</div>')($scope).html()

我认为你创建一个指令比使用过滤器更好,因为你需要编译$scope变量。

也许您可以使用ngBindTemplatengInclude