如何在不覆盖父范围的情况下将参数传递给指令

How does one pass an argument to a directive without over-writing parent scope?

本文关键字:情况下 参数传递 指令 范围 覆盖      更新时间:2023-09-26

我需要创建一个指令,该指令作用于使用ng-repeat呈现表格行的表格单元格 - 为此,我部分依赖于对题为"ng-repeat完成后调用函数"的问题的回答。然而,与问答不同的是,我需要向我的指令传递一个参数,为此我部分依赖于这个答案(标题为"Angularjs - 将参数传递给指令"的问题(。

因此,就我而言,我为我的指令添加了fixed-column-tooltip,并columnselector作为其参数到<tr>,如下所示:

<tr fixed-column-tooltip columnselector=".td-keyField" ng-repeat="trData in trDataWatch">

但是,当根据第二个答案时,我将我学到的"隔离范围"添加到我的指令中,我不再能够访问第一个答案所需的原始范围:

'use strict';
angular.module('cmt.cases.directives')
.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
            columnselector: '@'
        },
        link: function (scope, element, attr) {
            if (scope.$last === true) { //undefined because not operating on original scope
        ...

有没有办法保持对原始范围的访问,同时也可以访问columnselector参数?

你可以

使用,

'use strict';
angular.module('cmt.cases.directives')
.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
            columnselector: '@',
            $last: '=$last',
        },
        link: function (scope, element, attr) {
            if (scope.$last === true) {
            ....

作用域的第二个参数将通过引用传递$last参数。

编辑:

由于$last仅在重复元素的范围内可用,因此您可以从元素范围获取它,如下所示

'use strict';
angular.module('cmt.cases.directives')
.directive('fixedColumnTooltip', function ($timeout) {
return {
    srestrict: 'A',
    scope: {
        columnselector: '@',
    },
    link: function (scope, element, attrs) {
      var elemScope = element.scope();
      if (elemScope.$last){
             ......
      }          
    }
}

好的,首先,仅仅因为您使用的是隔离作用域并不意味着您无法访问父作用域中的某些内容。 隔离作用域旨在限制默认情况下获取的内容,但你可以指定所需的父作用域中的任何内容。 正确的方法是使用"parentScopeVariable: '='"在指令中设置双向绑定。 原谅我在移动设备上的可怕格式,我想上床睡觉:-(。

所以是的,就像你说的,你也可以使用"attrs"参数,当然。 甚至还有在父范围上设置内容的棘手$eval方法,这些方法仅作为 attr 传入。 无论如何,您不能在给定元素/组件上有多个具有隔离作用域的指令,因此在使用隔离作用域时确实需要小心。 它绝对适合干净的设计,因为您必须仔细考虑指令中使用的内容。 关键是,在我看来,有时依靠吸引力是很好和必要的。 诚然,它确实感觉有点黑客或其他什么(思考代码的味道(,但我认为没有强有力的理由。

最后,我在 Angular API 文档网站上花费了大量时间,那里有很多好东西。 在$compile服务页面上有一个非常好的指令参考。 再次,移动,对不起。 如果我在一台完整的计算机上,我会做很好的代码块并链接指令 ref,对不起:-(。快速谷歌一下,你会发现它。

因此,您绝对可以使用隔离作用域,并且有一些方法可以将函数调用传递回指令,将指令函数引用从指令中传递回控制器,双向数据绑定等。 隔离范围非常适合所有这些,但听起来您并没有试图做任何太复杂的事情。

尽管对

Angular 几乎是全新的,但我正在回答我自己的问题,但仍然想要额外的答案,以防我解决问题的方式不被认为是"惯用的"Angular。

具体来说,我没有使用隔离范围,而是在下面的代码中利用了第三个attrs(属性(链接/函数参数,否则将新的 columnselector 属性与我的指令一起获取到 html。 这是一种普遍接受的做法吗?

'use strict';
angular.module('cmt.cases.directives')
.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            if (scope.$last === true) {
                $timeout(function () {
                    element.parent().find(attrs.columnselector).each(function() {
                        var td = $(this);
                        var span = td.find('span');
                        if (span.width() > td.width()){
                            span.attr('data-toggle','tooltip');
                            span.attr('data-placement','right');
                            span.attr('title', span.html());
                        }
                    });
                });
            }
        }
    }
});

补遗:正如您从评论中看到的那样,尽管尝试了几种不同的方法,但我仍无法使此答案中的代码正常工作。 如果我在合并该答案方面做错了什么,请告诉我。

与此同时,我找到了另一种方法,但这几乎可以肯定更像是一种"代码气味",而不是利用attrs论点。 具体来说,我发现我可以使用隔离范围,然后访问该范围的 $parent 范围属性。 然后我会像下面这样开始我的代码,但我并不提倡这一点,而只是注意到它,因为似乎 TMTOWTDI(有多种方法可以做到这一点(当然适用于 Angular:

'use strict';
angular.module('cmt.cases.directives')
.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
            columnselector: '@'
        },
        link: function (scope, element, attrs) {
            if (scope.$parent.$last === true) {
                $timeout(function () {
                    element.parent().find(scope.columnselector).each(function() {
                    ...

在HTML模板的Angular框架中,您可以访问父范围。

例如:

<div ng-model="$parent.$parent.theModel"></div>

当您在模板中创建新范围时,例如 ng-repeat 等,这很有用。理论上,您可以使用它来访问要使用的父范围。

可能

有点丑,但工作:获取当前指令的 DOM 元素,向后遍历到其父元素,使其成为角度元素,在其上调用内置的 scope(( 函数,例如

link: function (scope, elem) {
  var parentScope = angular.element ($(elem).parent()).scope();
  console.log (parentScope)
}

触摸父范围可能不是最好的主意(我的意思是这不是访问不同层的角度方式(,最好有一些额外的scope.models。无论如何,这是一个简单的工作演示。

angular.module('app', [])
.controller('ctrl', function($scope){
  $scope.trDataWatch = ['item1', 'item2', 'item3'];
  $scope.state = 'unrendered';
  $scope.$on('ngRepeatFinished', function(){
     $scope.state = 'ngRepeatFinished';
  });
})
.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
          columnselector: '@',
          first: '=?',
          middle: '=?',
          last: '=?',
          index: '=?',
          odd: '=?',
          even: '=?',
          
        },
        link: function (scope, element, attr) {
          if(scope.last){
              scope.$emit('ngRepeatFinished');
          }
        }
    };
});
td {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  <h4>{{state}}</h4>
  
<table>
  <tr fixed-column-tooltip columnselector=".td-keyField"
      ng-repeat="trData in trDataWatch"
      index="$index"
      odd="$odd"
      even="$even"
      first="$first"
      middle="$middle"
      last="$last">
    <td>{{trData}}</td>
  </tr>
</table>
</div>

但我强烈建议你重新设计逻辑

据我了解,您只想显示范围工具提示,td更宽,您绝对应该使用另一个指令,而在第二个指令中,需要第一个指令,以便您可以使用它的控制器逻辑,或者其他什么。无论如何 - 更好的设计会帮助你更好,所以你最好更深入地思考

如果要将控制器作用域与 in 指令一起使用,则应执行以下操作

app.directive('fixedColumnTooltip', function ($timeout) {
return {
    restrict: 'A',
    link: function (scope, element, attr) {
      var columnselector = attr.columnselector;
      console.log(scope[columnselector]);
    }
}});

这不会为指令创建任何范围,您仍然可以访问列选择器的值。如果你想在 columselector 中传递函数,那么你可以做$parse(attr.columnselector(。如果它是一个值,则不需要$parse。

在指令中定义作用域时,将创建一个隔离作用域。传入$last变量的最简单方法是作为另一个属性:

<tr fixed-column-tooltip columnselector=".td-keyField" ng-repeat="trData in trDataWatch" last="$last">

您的指令范围如下所示:

scope: {
    columnselector: '@',
    $last: '=last'
}

或者,您可以简单地访问链接函数中的父范围:

link: function (scope, element, attr) {
    if (scope.$parent.$last === true) { // Will evaluate true one time
    }
}

在这种情况下,您将不需要其他属性,也不需要在指令范围内定义$last。JSFiddle

简单地说。在链接函数上使用属性参数...

link: function(scope, element, attributes, ctrl) {
  var selector = attributes.columnselector;
}

我不知道为什么我读了大量的答案,认真的伙计们。