Javascript 带隔离作用域的AngularJS指令-我真的必须到处调用$parent吗?

Javascript 带隔离作用域的AngularJS指令-我真的必须到处调用$parent吗?,javascript,angularjs,Javascript,Angularjs,我有一个angular.js指令来创建按钮(悬停类、左图标和右图标等)。我通过作用域:{uiButtonIconLeft:'@',uiButtonIconRight:'@'}使用按钮左右图标的自动绑定,以便将这些值绑定到父作用域中的数据。然而,这会导致angularjs创建一个“隔离”范围,这意味着在这样的情况下使用我的指令不起作用: <div ng-controller='someController'> <a ng-repeat='thing in things'

我有一个angular.js指令来创建按钮(悬停类、左图标和右图标等)。我通过
作用域:{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.
吗?

编辑


我为我的按钮“widget”确定的答案是避免使用隔离作用域,并通过
属性查看左右图标的属性值。$observe(…)
而不是通过作用域绑定。

@
仅适用于本地作用域属性。请尝试
&
,这样可以在父作用域的上下文中执行表达式

引自

&
&attr
-提供在 父范围。如果未指定属性名,则属性名 假定与本地名称相同。给定
和小部件范围定义:
{
localFn:'&myAttr'}
,然后隔离作用域属性
localFn
将指向
count=count+value
表达式的函数包装器。经常 希望通过表达式从隔离范围传递数据 对于父作用域,这可以通过传递本地 将变量名和值放入表达式包装器fn中。例如 如果表达式是
increment(amount)
,那么我们可以指定
amount
通过调用
localFn
作为
localFn({amount:22})


约翰·林德奎斯特(John Lindquist)在其网站视频的17、18和19中对此进行了详细介绍。

我想我理解你的意图。在您的指令中,只需配置隔离作用域,以便通过ng click属性映射到父级上所需的函数

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

有一种不使用显式作用域的方法,如果您编写的指令没有专门处理作用域中的元素,最好这样做:

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

}


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

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

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

我们编写的任何自定义指令都需要记录创建的范围类型。例如,Angular文档指示哪些指令创建新范围。有关此问题的更多讨论,请参阅此答案的评论:


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

尽可能避免使用
$parent
,这会降低灵活性。如果你深入,那么你必须做
$parent.$parent.$parent…
,这是非常痛苦的。看看我的答案,没有隔离作用域,我不想要隔离作用域。我还是不太明白当初拥有它们的原因。我想我可以使用
$scope.watch(…)
来达到我想要的效果,而不是编写指令来使用隔离作用域。@FMM,如果您想编写一个可重用的组件/指令来创建自己的作用域属性,隔离作用域非常有用。对于隔离作用域,您不必担心意外使用(父)作用域中已存在的作用域属性名称。因此,隔离作用域提供了安全性(作用域安全性)。如果不需要隔离作用域,有两个选项:1)不创建任何作用域-恢复元素的作用域2)使用
scope:true
创建原型继承的子作用域。这两个选项都不需要使用$parent。感谢您的额外输入,它提供了非常丰富的信息。您获得了一个向上投票,但
属性。$observe(…)
是我真正需要的。避免隔离范围似乎是我需要的,谢谢!我在指令#2中遇到了“$digest ready in progress”错误:刚开始熟悉AngularJS,这是我真正不喜欢的第一件事,因为我可能在某个时候在指令中有嵌套指令和更多控制器-非常棒的解决方案,谢谢!
val+=“Hello”
应该做什么?这甚至不是有效的JavaScript语法。。。
<img ui-button ui-button-left="{{item.leftBtn}}"></img>
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>