【问题标题】:Custom Directive fails to compile within ng-repeat自定义指令无法在 ng-repeat 中编译
【发布时间】:2017-07-29 17:32:34
【问题描述】:

在 ng-repeat 中编译指令时,谁能帮我解决作用域问题?

https://plnkr.co/edit/y6gfpe01x3ya8zZ5QQpt?p=preview

自定义指令input-by-type 可以根据变量类型将<div> 替换为适当的<input> - 这在ng-repeat 中使用之前可以正常工作。

正如您在 plnkr 示例中所见,该指令按预期工作,直到在 ng-repeat 中使用。

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
    $scope.data = {};
    $scope.inputs = [
        { name: 'Some Text', type: 'text',   id: 1 },
        { name: 'EMail',     type: 'email',  id: 2 },
        { name: 'Age',       type: 'number', id: 3 }
    ];
});

app.directive('inputByType', ['$compile', '$interpolate', function($compile, $interpolate){
    return {
        restrict: 'A', // [attribute]
        require: '^ngModel',
        scope: true,
        compile: function(element, attrs, transclude){
            var inputs = {
                text:    '<input type="text"  name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...">',
                email:   '<input type="email" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...@...">',
                number:  '<input type="number" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="###">',
            };
            return function(scope){
                var type = $interpolate(attrs.inputByType)(scope); // Convert input-by-type="{{ some.type }}" into a useable value
                var html = inputs[type] || inputs.text;
                var e = $compile(html)(scope);
                element.replaceWith(e);
                console.log(type, html, element, e);
            };
        },
    };
}]);

如果我手动引用inputs[0] 来编译input-by-type 指令,它就可以正常工作:

<label>
    {{ inputs[0].name }}
    <div input-by-type="{{ inputs[0].type }}" name="myInputA" ng-model="data.A" ng-required="true"></div>
</label>

但是,当我将其包装在 ng-repeat 块中时,编译失败并出现一些意外输出:

<label ng-repeat="input in inputs">
    {{ input.name }}
    <div input-by-type="{{ input.type }}" name="myInput{{ $index }}" ng-model="data[input.id]" ng-required="true"></div>
</label>

预期输出:


实际输出:

【问题讨论】:

    标签: angularjs angularjs-directive angularjs-compile


    【解决方案1】:

    @georgeawg 的回答是正确的,但是我遇到了第二个问题,我将在下面给出一个解决方案。

    问题:ngModel 无法按预期运行($pristine / $dirty 等属性将不可用,也不会传播到容器 formCtrl)。

    为了解决这个问题,我遵循了关于此答案的建议:https://stackoverflow.com/a/21687744/1122851 并更改了 postLink 编译元素的方式,如下所示:

    var type = $interpolate(attrs.inputByType)(scope);
    var html = inputs[type] || inputs.text;
    var template = angular.element(html);
    element.replaceWith(template);
    $compile(template)(scope);
    

    然后我意识到 require: 'ngModel'scope: trueterminal: true 不再需要(无论如何,它们是我各种测试的遗物)。最终代码:

    app.directive('inputByType', ['$compile', '$interpolate', function($compile, $interpolate){
        return {
            restrict: 'A', // [attribute]
            compile: function(element, attrs, transclude){
                var inputs = {
                    text:    '<input type="text"  name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...">',
                    email:   '<input type="email" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...@...">',
                    number:  '<input type="number" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="###">',
                    // image upload (redacted)
                    // file upload (redacted)
                    // date picker (redacted)
                    // color picker (redacted)
                    // boolean (redacted)
                };
                return function postLinkFn(scope, element, attrs) {
                    var type = $interpolate(attrs.inputByType)(scope); // Convert input-by-type="{{ some.type }}" into a useable value
                    var html = inputs[type] || inputs.text;
                    var template = angular.element(html);
                    element.replaceWith(template);
                    $compile(template)(scope);
                };
            },
        };
    }]);
    

    演示:https://plnkr.co/edit/ZB5wlTKr0g5pXkRTRmas?p=preview

    【讨论】:

      【解决方案2】:

      postLink函数缺少elementattrs参数:

      app.directive('inputByType', ['$compile', '$interpolate', function($compile, $interpolate){
          return {
              restrict: 'A', // [attribute]
              require: '^ngModel',
              scope: true,
              // terminal: true,
              compile: function(element, attrs, transclude){
                  var inputs = {
                      text:    '<input type="text"  name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...">',
                      email:   '<input type="email" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="...@...">',
                      number:  '<input type="number" name="'+attrs.name+'" ng-model="'+attrs.ngModel+'" ng-disabled="'+attrs.ngDisabled+'" ng-required="'+attrs.ngRequired+'" placeholder="###">',
                      // image upload (redacted)
                      // file upload (redacted)
                      // date picker (redacted)
                      // color picker (redacted)
                      // boolean (redacted)
                  };
                  //return function(scope){
                  //USE postLink element, attrs
                  return function postLinkFn(scope, element, attrs) {
                      var type = $interpolate(attrs.inputByType)(scope); // Convert input-by-type="{{ some.type }}" into a useable value
                      var html = inputs[type] || inputs.text;
                      var e = $compile(html)(scope);
                      element.replaceWith(e);
                      console.log(type, html, element, e);
                  };
              },
          };
      }]);
      

      通过省略elementattrs 参数,postLink 函数创建了一个闭包并使用compile 函数的elementattrs 参数。即使 $compile 服务使用正确的参数调用 postLink 函数,它们也会被忽略,而是使用编译阶段版本。

      这会导致 ng-repeat 出现问题,因为它会克隆元素以便将其附加到新的 DOM 元素。

      【讨论】:

      • 我知道 postLink 参数,但我并没有意识到它们可能会因阶段而异。谢谢。
      猜你喜欢
      • 2016-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-26
      • 1970-01-01
      • 1970-01-01
      • 2014-01-24
      • 2015-03-06
      相关资源
      最近更新 更多