3

我有一个 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>

编辑

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

4

4 回答 4

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>
于 2013-02-13T17:25:03.790 回答
5

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

引用自http://docs.angularjs.org/guide/directive

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

John Lindquist 在他的网站egghead.io视频的 17、18 和 19 中很好地介绍了这一点。

于 2013-02-13T17:01:29.257 回答
2

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

通常,您不必在 ng-repeat 中使用 $parent,因为 ng-repeat 通常会创建一个子范围,该子范围在原型上继承自父/控制器范围。因此,在 ng-repeat 中调用方法会沿着原型链到达父作用域以找到该方法。

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

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

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

于 2013-02-13T18:14:04.000 回答
2

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

scope: {
    uiButtonIconLeft: '@',
    uiButtonIconRight: '@',
    clicky: '&ngClick'
}
于 2013-02-13T17:02:24.100 回答