AngularJS:使隔离范围指令模板绑定到父范围 [英] AngularJS: Make isolate scope directive template bind to parent scope

查看:18
本文介绍了AngularJS:使隔离范围指令模板绑定到父范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

查看下面的示例代码:

指令:

directives.directive('productAttributeWrapper', ['$compile', function($compile){//此指令的存在仅用于提供对父作用域的productAttribute"指令访问返回 {限制:'A',范围:假,控制器:函数($scope,$element,$attrs){this.compile = 函数(元素){$compile(element)($scope);console.log('$scope.prodAttribs in 指令:', $scope.prodAttribs);};}}}]);directives.directive('productAttribute', ['$compile', function($compile){返回 {限制:'A',require: '^productAttributeWrapper',//使用包装器的控制器范围: {attribModel: '=',prodAttribute: '=productAttribute',//绑定到 ng-repeat 迭代的模型},链接:函数(范围,元素,属性,ctrl){var 模板 = '';var attr = scope.prodAttribute;if(!attr) 返回;开关(attr.attribute_field_type.toLowerCase()){案例文本字段":模板 ='<input type="text" id="'+attr.attribute_code+'" ng-model="vm.prodAttribs.'+ attr.attribute_code +'">';休息;案例下拉":模板 = ['<select class="cvl" id="'+attr.attribute_code+'" ng-model="vm.prodAttribs.'+ attr.attribute_code +'">','#cvl_option_values','\n'].加入('');var options = '\n';for(var i=0; i'+ attr.cvl_option_values[i].value + '</option>';}template = template.replace('#cvl_option_values', options);休息;}element.html(模板);ctrl.compile(element.html());//尝试将模板绑定到外部作用域}}}]);

html:

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

控制器:

app.controller('ProductDetailsController', function(){var vm = 这个;//还将属性添加到 $scope 以查看我是否可以在那里访问它$scope.prodAttribs = vm.prodAttribs = {姓名: '',描述: '',价格:[0.0],条件:空}vm.all_attributes = [{attribute_id":1210,"attribute_display_name": "产品类型","attribute_code": "product_type","attribute_field_type": "文本字段","cvl_option_values": [],validation_rules":{}},{attribute_id":902,"attribute_display_name": "增值税","attribute_code": "增值税","attribute_field_type": "下拉菜单",cvl_option_values":[{"option_id": "5",价值":5%"},{"option_id": "6",价值":豁免"}],validation_rules":{}}];})

解决方案

问题可能在这里:

element.html(模板);ctrl.compile(element.html());//尝试将模板绑定到外部作用域

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 行.

I've been struggling with Angular's isolate scope for over 24hrs now. Here's my scenario: I have an ng-repeat iterating over an array of objects from which I want to use a custom directive to either generate a <select> or <input> based on the field_type property of the current object being iterated. This means I'll have to generate the template and $compile in the post-link function of the directive since I have no access to the iterated object in the template function.

Everything works as expected, apart from the actual binding of the generated template to the controller (vm) in my outer scope. I think my approach (adding this in the template string: ng-model="vm.prodAttribs.' + attr.attribute_code +'") may be wrong, and would appreciate pointers in the right direction. Thanks!

See sample code below:

directives:

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>

controller:

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": {}
    }];
})

解决方案

issue is probably here :

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

element.html() returns a html as a string, not the ACTUAL dom content, so what you inserted into your directive's element is never actually compiled by angular, explaining your (absence of) behaviour.

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

should work way better.

For directive requiring parent controller, I would also change your ctrl.compile method (renamed to insertAndCompile here)

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

You would just have to call it this way :

ctrl.insertAndCompile(template);

instead of the 2 lines I gave as first answer.

这篇关于AngularJS:使隔离范围指令模板绑定到父范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆