如何在不重写父作用域的情况下将参数传递给指令?

时间:2016-01-08 21:08:20

标签: javascript angularjs angularjs-scope angularjs-ng-repeat angular-directive

我需要创建一个指令,该指令作用于使用ng-repeat呈现表行的表格单元格 - 为此我在this answer部分依赖于题为“调用函数”的问题当ng-repeat完成时“。然而,与Q& A不同,我需要向我的指令传递一个参数,为此我部分地依赖于this answer(对于一个题为“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参数吗?

9 个答案:

答案 0 :(得分:3)

你可以用,

'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仅在repeat元素的范围内可用,你可以从元素范围中获取它,比如

'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){
             ......
      }          
    }
}

答案 1 :(得分:1)

尽管对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());
                        }
                    });
                });
            }
        }
    }
});

附录: 正如您从评论中看到的那样,尽管尝试了几种不同的方法,我还是无法从this answer获得代码。如果我在整理答案方面做错了,请告诉我。

与此同时,我找到了另一种方法,但这几乎肯定比利用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() {
                    ...

答案 2 :(得分:1)

好吧首先,仅仅因为您使用隔离范围并不意味着您无法访问父范围内的某些内容。隔离范围旨在限制默认情况下获得的内容,但您可以从父范围指定所需内容。正确的方法是使用&#34; parentScopeVariable:&#39; =&#39;&#34;在您的指令中设置双向绑定。原谅我在手机上的可怕格式,我想上床睡觉:-)。

所以是的,就像你说你可以使用&#34; attrs&#34;参数也是,当然。甚至有一些棘手的$ eval方式在父作用域上设置东西,只作为attrs传递。无论如何,您不能在给定元素/组件上具有多个具有隔离范围的指令,因此在使用隔离范围时确实需要注意。它绝对有助于清洁设计,因为你必须仔细考虑你在指令中使用的内容。在我看来,依靠attrs很有必要。不可否认,它确实感觉有点hackish或其他任何东西(思考代码味道),但我不认为那是一个强有力的理由。

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

所以你绝对可以使用隔离范围,并且有办法将函数回调传递给指令,将指令函数引用传递出指令,返回控制器,双向数据绑定等。隔离范围很好尽管如此,但听起来并不像你想做任何太复杂的事情。

答案 3 :(得分:0)

在HTML模板的Angular框架中,您可以访问父作用域..

例如:

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

当您在模板中创建新范围时,这会起作用,例如ng-repeat等。理论上,您可以使用它来访问您希望使用的父作用域。

答案 4 :(得分:0)

可能有点难看,但工作: 获取当前指令的DOM元素,向后遍历其父元素,使其成为一个角元素,在其上调用内置的scope()函数,例如

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

答案 5 :(得分:0)

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

&#13;
&#13;
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');
          }
        }
    };
});
&#13;
td {
  display: block;
}
&#13;
<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>
&#13;
&#13;
&#13;

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

据我所知,你只想显示td的span-tooltips,更宽,你绝对应该使用另一个指令,而在第二个,需要第一个指令,以便你可以使用它控制器逻辑,或其他什么。无论如何 - 更好的设计会更好地帮助你,所以你更好地思考

答案 6 :(得分:0)

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

app.directive('fixedColumnTooltip', function ($timeout) {
return {
    restrict: 'A',

    link: function (scope, element, attr) {
      var columnselector = attr.columnselector;
      console.log(scope[columnselector]);
    }
}});

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

答案 7 :(得分:0)

在指令中定义范围时,您将创建隔离范围。传递$ 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

答案 8 :(得分:0)

简单。在链接函数中使用属性参数...

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

我不知道为什么我会仔细阅读广泛的答案。