AngularJS:嵌套指令和范围继承

时间:2014-07-18 15:17:44

标签: javascript angularjs angularjs-directive angularjs-scope

序言:看起来这个问题之前已经被问过并得到了回答,但我似乎无法让它工作,所以如果我的问题归结为"你可以调试我的代码吗?" ,我道歉。


我想写下面的代码:

<radio-set ng-model="obj.prop" name="obj_prop">
    <radio-set-button ng-value="'public'">Public</radio-set-button>
    <radio-set-button ng-value="'protected'">Protected</radio-set-button>
    <radio-set-button ng-value="'private'">Private</radio-set-button>
</radio-set>

这会呈现一堆单选按钮和标签,需要填充传递给<radio-set>的ngModel的内容。我遗漏了与范围相关的内容。

.directive("radioSet", function () {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            ngModel: '=?',
            ngChange: '&',
            name: '@'
        },
        transclude: true,
        template: '<div class="radio-set" ng-transclude></div>',
        controller: function () {}
    };
})
.directive("radioSetButton", function () {
    return {
        restrict: 'E',
        replace: true,
        require: ['^radioSet', '?ngModel'],
        scope: {
            ngModel: '=?', // provided by ^radioSet?
            ngValue: '=?',
            ngChange: '&', // provided by ^radioSet?
            name: '@'      // provided by ^radioSet?
        },
        transclude: true,
        link: function (scope, element, attr) {
            element.children().eq(0).attr("name", scope.name); // scope.name is null
        },
        template: '<label class="radio-set-button">' +
                    '<input type="radio" name="name" ng-model="ngModel" ng-value="ngValue" ng-change="ngChange()">' +
                    '<div class="radio-content" ng-transclude></div>' +
                  '</label>'
    };
})

父指令和子指令都需要自己的范围定义,但我不清楚如何从radioSet内访问radioSetButton的范围。

感谢您的帮助。

小提琴:http://jsfiddle.net/pmn4/XH5K2/2/

1 个答案:

答案 0 :(得分:2)

Transclusion

我想我必须告诉你,你在指令中使用的翻译并不像你期望的那样有效,因为简而言之:被转换的指令并没有继承你期望的范围,实际上它继承了外部控制器的范围,但是在这个主题上有很多答案:

Access Parent Scope in Transcluded Directive

如何解决

要访问父母指令,基本上有两种方法:

1。)需要父母指令的控制器并创建一些API来做父母的事情

2.。)使用link函数的第五个参数来访问transclude函数,在这里你可以改变注入的范围,你可以将它设置为parent指令范围

由于第一个解决方案更直观,我会选择这个:

radioSet指令中,我在Controller中设置了对象的双向数据绑定,并创建了一个getter和setter方法来与值进行交互。

在&#34; child&#34;的指令中,我需要父指令的控制器,我将其作为链接函数中的第四个参数传递。我在元素上设置了一个点击处理程序来获取点击,在这里我用我的值调用父设置器方法。为了可视化当前所选对象,我添加了一个ng-class指令,它有条件地添加了活动类。

注意:这样您也可以使用ngModel指令。它有一个与模型交互的API。

第二个解决方案使用transclude函数,您可以使用该函数传入范围。由于我现在没有时间,因为它增加了更多的复杂性,我建议使用第一个解决方案。

建议

对于您的示例,转换可能不是正确的选择,请使用一个指令并将选项添加到模板或将它们传递给指令。因为我不知道你的意图是什么,我提供了这个解决方案。 (我不知道这个名称属性的用途是什么?)

代码

小提琴:http://jsfiddle.net/q3nUk/

样板:

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

app.controller('MainController', function($scope) {
    $scope.object = {
        'property' : 'public'
    };
});

指令:

app.directive('radioSet', function() {
    return {
        scope : {
            radioValue : '='
        },
        restrict : 'E',
        transclude : true,
        replace : true,
        template : '<div class="radioSet" ng-transclude></div>',
        controller : function($scope) {

            this.getRadioValue = function() {
                return $scope.radioValue;
            }

            this.setRadioValue = function(val) {
                $scope.$apply(function() {
                    $scope.radioValue = val
                });
            }
        }
    };
});

app.directive('radioSetButton', function() {
    return {
        restrict : 'E',
        transclude : true,
        replace : true,
        scope : true,
        template : '<div class="radioSetButton" ng-class="{active:isActive()}" ng-transclude></div>',
        require : '^radioSet',
        link : function(scope, elem, attrs, radioSetController, transclude) {
            scope.isActive = function() {
                return attrs.buttonValue === radioSetController.getRadioValue();
            };

            elem.on('click', function() {
                radioSetController.setRadioValue(attrs.buttonValue);
            });
        }
    };
});

HTML:

<html>
<body ng-app="myApp">

    <div ng-controller="MainController">
        <p>{{ object.property }}</p>

        <radio-set radio-value="object.property">
            <radio-set-button button-value="public">Public</radio-set-button>
            <radio-set-button button-value="private">Private</radio-set-button>
            <radio-set-button button-value="protected">Protected</radio-set-button>
        </radio-set>

    </div>

</body>
</html>

CSS:

.radioSetButton {
    display : block;
    padding : 10px;
    border : 1px solid black;
    float : left;
}

.radioSetButton:hover {
    cursor : pointer;
}

.radioSetButton.active {
    background-color : grey;
}