具有隔离作用域的 AngularJS 指令 - 我真的必须在任何地方调用$parent

AngularJS directive with isolate scope - do I really have to call $parent everywhere?

本文关键字:方调用 任何地 调用 parent 作用域 隔离 AngularJS 指令 真的      更新时间:2023-09-26

我有一个角度.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(...)观察左右图标的属性值,而不是通过范围绑定。

有一种方法可以在不使用显式作用域的情况下做到这一点,如果你正在编写一个不专门处理作用域中元素的指令,最好这样做:

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>

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

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

&&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页。

我想

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

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

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

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

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

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

另一个选项是将指令更改为不使用隔离作用域,并使用属性来指示指令应检查哪些作用域属性。