具有隔离范围的AngularJS指令 - 我真的必须在任何地方调用$ parent吗?

时间:2013-02-13 16:51:30

标签: javascript angularjs

我有一个angular.js指令来创建一个按钮(悬停类,左图和右图等)。我正在通过scope: { uiButtonIconLeft: '@', uiButtonIconRight: '@' }使用按钮的左右图标的自动绑定,以便我可以将这些值绑定到父作用域的数据。但是,这会导致angularjs创建一个“隔离”范围,这意味着在这样的情况下使用我的指令不起作用:

<div ng-controller='someController'>
    <a ng-repeat='thing in things'
       ui-button
       ui-button-icon-left='{{thing.icon}}'
       ng-click='someMethodTheControllerPutOnTheScope(thing.id)'
       >
       I don't work, don't bother clicking me
    </a>
</div>

我必须这样做:

<div ng-controller='someController'>
    <a ng-repeat='thing in things'
       ui-button
       ui-button-icon-left='{{thing.icon}}'
       ng-click='$parent.someMethodTheControllerPutOnTheScope($parent.thing.id)'
       >
       Holy leaky abstractions, Batman, it works!
    </a>
</div>

我的问题是:这是惯用的吗?应该这样吗?我做错了吗?我们的英雄可以在他的标记中清除多余的,重复的,恼人的额外$parent.<whatever>吗?

修改

我为我的按钮“widget”确定的答案是避免使用隔离范围并通过attributes.$observe(...)观察左右图标的属性值,而不是通过范围进行绑定。

4 个答案:

答案 0 :(得分:10)

有一种方法可以在不使用显式范围的情况下完成它,如果您正在编写一个不专门处理范围内元素的指令,最好这样做:

function MyDirective () {
    return {
        link: function (scope, iElement, iAttrs) {
            iAttrs.$observe("uiButtonLeft", function (val) {
                if (val) {
                    iElement.attr(src, val); // whatever you want to do
                }
            });
        }

}

<img ui-button ui-button-left="{{item.leftBtn}}"></img>

有时候使用{{val}}变得很麻烦,无论是对于干净的代码,还是您可能还想要更改val。这是你如何做到的。

function MyDirective () {
    return {
        link: function (scope, iElement, iAttrs) {
            iAttrs.$observe("uiButtonLeft", function (val) {
                scope.$watch(val, function (valInScope) {
                    if (valInScope) {
                        iElement.attr(src, valInScope); // whatever you want to do

                        // the following statement updates the value in scope
                        // it's kinda weird, but it works.
                        scope.$apply(val + "= Hello"); // if you are out of angularjs, like jquery event
                        scope.$eval(val + = "Hello"); // if you are in angualrjs
                        // $apply can handle string, it's like ngClick
                        // you use in templates. It updates the value correctly.
                    }
                }
            });
        }

}

<img ui-button ui-button-left="item.leftBtn"></img>

答案 1 :(得分:5)

@仅适用于本地范围属性。请尝试使用&,这允许您在父作用域的上下文中执行表达式。

来自http://docs.angularjs.org/guide/directive

  

&&attr - 提供了一种在上下文中执行表达式的方法   父范围。如果未指定attr名称,则为属性名称   假定与本地名称相同。给定<widget my-attr="count = count + value">和范围的小部件定义:{ localFn:'&myAttr' },然后隔离范围属性localFn将指向   count = count + value表达式的函数包装器。经常   希望通过表达式从隔离范围传递数据   对于父作用域,可以通过传递本地地图来完成   变量名和值到表达式包装器fn中。例如,   如果表达式为increment(amount),那么我们可以指定amount   将localFn称为localFn({amount: 22})

,即可获得价值

John Lindquist在他的网站egghead.io视频的17,18和19上详细介绍了这一点。

答案 2 :(得分:2)

我想我明白你要做什么。在您的指令中,只需将隔离范围配置为通过ng-click属性映射到您想要的功能。

scope: {
    uiButtonIconLeft: '@',
    uiButtonIconRight: '@',
    clicky: '&ngClick'
}

答案 3 :(得分:2)

如果您希望您的指令使用隔离范围,并且您想要从HTML /标记中的同一元素调用父/控制器范围上定义的方法,那么使用$ parent就可以了。

通常,您不必在ng-repeat中使用$ parent,因为ng-repeat通常会创建一个原型继承父/控制器范围的子范围。因此,在ng-repeat中调用一个方法跟随原型链到父范围以找到方法。

由于您的指令创建了隔离范围,因此每个ng-repeat迭代都被强制使用相同的隔离范围而不是通常使用的范围,因为它们是在同一元素上定义的。获取父作用域(从HTML)定义的方法的唯一方法是使用$ parent,因为没有原型链可以从隔离范围中跟随。

我们编写的任何自定义指令都需要记录创建哪种范围。例如,Angular文档指出哪些指令创建新范围。有关此问题的更多讨论,请参阅此答案的评论:https://stackoverflow.com/a/14345214/215945

您的另一个选择是将指令更改为不使用隔离范围,并使用属性指示指令应检查的范围属性。