【问题标题】:AngularJS: Make isolate scope directive template bind to parent scopeAngularJS:使隔离范围指令模板绑定到父范围
【发布时间】:2016-01-12 13:35:39
【问题描述】:

我已经为 Angular 的隔离作用域苦苦挣扎了超过 24 小时。这是我的场景:我有一个 ng-repeat 迭代一个对象数组,我想使用自定义指令从其中生成一个 <select> 或 <input> 基于被迭代的当前对象的 field_type 属性。这意味着我必须在指令的链接后函数中生成模板和$compile,因为我无法访问模板函数中的迭代对象。

除了将生成的模板实际绑定到我的外部范围内的控制器 (vm) 之外,一切都按预期工作。我认为我的方法(在模板字符串中添加这个:ng-model="vm.prodAttribs.' + attr.attribute_code +'")可能是错误的,并且希望得到正确方向的指针。谢谢!

参见下面的示例代码:

指令:

directives.directive('productAttributeWrapper', ['$compile',  function($compile){
    //this directive exists solely to provide 'productAttribute' directive access to the parent scope
    return {
        restrict: 'A',
        scope: false,
        controller: function($scope, $element, $attrs){
            this.compile = function (element) {
                $compile(element)($scope);
                console.log('$scope.prodAttribs in directive: ', $scope.prodAttribs);
            };
        }
    }
}]);

directives.directive('productAttribute', ['$compile',  function($compile){
    return {
        restrict: 'A',
        require: '^productAttributeWrapper', //use the wrapper's controller
        scope: {
            attribModel: '=',
            prodAttribute: '=productAttribute', //binding to the model being iterated by ng-repeat
        },
        link: function(scope, element, attrs, ctrl){
            var template = '';
            var attr = scope.prodAttribute;
            if(!attr) return;

            switch(attr.attribute_field_type.toLowerCase()){
                case 'textfield':
                    template = 
                        '<input type="text" id="'+attr.attribute_code+'" ng-model="vm.prodAttribs.' + attr.attribute_code +'">';
                    break;
                case 'dropdown':
                    template = [
                        '<select class="cvl" id="'+attr.attribute_code+'" ng-model="vm.prodAttribs.' + attr.attribute_code +'">',
                            '#cvl_option_values',
                        '\n</select>'
                    ].join('');
                    var options = '\n<option value="">Select One</option>';
                    for(var i=0; i<attr.cvl_option_values.length; i++) {
                        var optionVal = attr.cvl_option_values[i].value;
                        options += '\n<option value="'+optionVal+'">' + attr.cvl_option_values[i].value + '</option>';
                    }
                    template = template.replace('#cvl_option_values', options);
                    break;
            }
            element.html(template);
            ctrl.compile(element.html());  //try to bind template to outer scope
        }
    }
}]);

html:

<div ng-controller="ProductController as vm">
    <div product-attribute="attrib" ng-repeat="attrib in vm.all_attribs"></div>
</div>

控制者:

app.controller('ProductDetailsController', function(){
    var vm = this;
    //also added the property to $scope to see if i could access it there
    $scope.prodAttribs = vm.prodAttribs = {
            name: '',
            description: '',
            price: [0.0],
            condition: null
    }
    vm.all_attributes = [
        {
          "attribute_id": 1210,
          "attribute_display_name": "Product Type",
          "attribute_code": "product_type",
          "attribute_field_type": "Textfield",
          "cvl_option_values": [],
          "validation_rules": {}
        },
        {
          "attribute_id": 902,
          "attribute_display_name": "VAT",
          "attribute_code": "vat",
          "attribute_field_type": "dropdown",
          "cvl_option_values": [
            {
              "option_id": "5",
              "value": "5%"
            },
            {
              "option_id": "6",
              "value": "Exempt"
            }
          ],
          "validation_rules": {}
    }];
})

【问题讨论】:

    标签: javascript angularjs angularjs-directive ng-repeat isolate-scope


    【解决方案1】:

    问题可能在这里:

    element.html(template);
    ctrl.compile(element.html());  //try to bind template to outer scope
    

    element.html() 将 html 作为字符串返回,而不是 ACTUAL dom 内容,因此您插入指令元素的内容永远不会被 angular 实际编译,从而解释您的(不存在)行为。

    element.append(ctrl.compile(template));
    

    应该会更好。

    对于需要父控制器的指令,我还将更改您的 ctrl.compile 方法(此处重命名为 insertAndCompile)

    ctrl.insertAndCompile = function(content) {
        $compile(content)($scope, function(clone) {
            $element.append(clone);
        }
    }
    

    你只需要这样称呼它:

    ctrl.insertAndCompile(template);
    

    而不是我作为第一个答案给出的 2 行。

    【讨论】:

    • 感谢@Pierre Gayvallet。 ctrl.insertAndCompile() 是我需要的修复程序!这么小的东西让我整晚都睡不着,这太痛苦了。 Stackoverflow 摇滚,你摇滚得更多。
    【解决方案2】:

    我建议使用模板而不是手动编译 html。 solution 要简单得多:

    控制器将包含数据声明:

    app.controller('ProductDetailsController', function($scope) {
      $scope.prodAttribs = {
        name: '',
        description: '',
        price: [0.0],
        condition: null
      }
      $scope.all_attribs = [{
        "attribute_id": 1210,
        "attribute_display_name": "Product Type",
        "attribute_code": "product_type",
        "attribute_field_type": "Textfield",
        "cvl_option_values": [],
        "validation_rules": {}
      }, {
        "attribute_id": 902,
        "attribute_display_name": "VAT",
        "attribute_code": "vat",
        "attribute_field_type": "dropdown",
        "cvl_option_values": [{
          "option_id": "5",
          "value": "5%"
        }, {
          "option_id": "6",
          "value": "Exempt"
        }],
        "validation_rules": {}
      }];
    });
    

    你的指令就这么简单:

    app.directive('productAttribute', function() {
      return {
        restrict: 'A',
        scope: {
          attribModel: '=',
          prodAttribute: '=productAttribute'
        },
        templateUrl: 'template.html',
        controller: function($scope) {}
    
      }
    });
    

    template.html 将是:

    <div>
      <select ng-show="prodAttribute.attribute_field_type.toLowerCase() == 'dropdown'" class="cvl" id="" ng-model="prodAttribs.attribute_code">
        <option value="">Select One</option>
        <option ng-repeat="item in prodAttribute.cvl_option_values track by $index"  value="{{item.value}}">{{item.value}}</option>
      </select>
      <input ng-show="prodAttribute.attribute_field_type.toLowerCase() == 'textfield'" type="text" id="{{prodAttribute.attribute_code}}" ng-model="prodAttribute.attribute_code">
    </div> 
    

    还有你的html:

    <div ng-controller="ProductController"> 
        <div ng-repeat="attrib in all_attribs" product-attribute="attrib">{{attrib}}</div>
    </div>
    

    【讨论】:

    • 我不想创建臃肿的 html,因为页面上会有很多这样的控件,导致很多不必要的隐藏输入元素。我为什么在链接函数中这样做的原因。非常感谢为这个答案付出的努力
    • 有道理,但是以这种方式创建指令太脏了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-30
    • 1970-01-01
    • 1970-01-01
    • 2014-05-20
    • 2014-04-28
    相关资源
    最近更新 更多