21

我创建了一个简单的指令,用于显示<table>我正在创建的排序列标题。

ngGrid.directive("sortColumn", function() {
    return {
        restrict: "E",
        replace: true,
        transclude: true,
        scope: {
            sortby: "@",
            onsort: "="
        },
        template: "<span><a href='#' ng-click='sort()' ng-transclude></a></span>",
        link: function(scope, element, attrs) {
            scope.sort = function () {

                // I want to call CONTROLLER.onSort here, but how do I access the controller scope?...
                scope.controllerOnSort(scope.sortby);
            };
        }
    };
});

下面是一些正在创建的表头的示例:

<table id="mainGrid" ng-controller="GridCtrl>
<thead>
    <tr>
        <th><sort-column sortby="Name">Name</sort-column></th>
        <th><sort-column sortby="DateCreated">Date Created</sort-column></th>
        <th>Hi</th>
    </tr>
</thead>

因此,当单击排序列时,我想在我的网格控制器上触发 onControllerSort 函数..但我被卡住了!到目前为止,我能够做到这一点的唯一方法是为 each <sort-column>,为“onSort”添加属性并在指令中引用这些属性:

<sort-column onSort="controllerOnSort" sortby="Name">Name</sort-column>

但这不是很好,因为我总是想调用controllerOnSort,所以为每个指令都插入它有点难看。如何在指令中执行此操作而不需要在我的 HTML 中进行不必要的标记?如果有帮助,指令和控制器都在同一个模块中定义。

4

5 回答 5

24

创建第二个指令作为包装器:

ngGrid.directive("columnwrapper", function() {
  return {
    restrict: "E",
    scope: {
      onsort: '='
    }
  };
});

然后您可以在外部指令中引用要调用一次的函数:

<columnwrapper onsort="controllerOnSort">
  <sort-column sortby="Name">Name</sort-column>
  <sort-column sortby="DateCreated">Date Created</sort-column>
</columnwrapper>

在“sortColumn”指令中,您可以通过调用调用该引用函数

scope.$parent.onsort();

请参阅此小提琴以获取工作示例:http: //jsfiddle.net/wZrjQ/1/

当然,如果您不关心硬编码的依赖项,您也可以使用一个指令,只需通过

scope.$parent.controllerOnSort():

我有另一个小提琴显示这个:http: //jsfiddle.net/wZrjQ/2

该解决方案与另一个答案(https://stackoverflow.com/a/19385937/2572897)中的解决方案具有相同的效果(在硬耦合方面具有相同的批评),但至少比该解决方案更容易. 如果你无论如何都很难耦合,我认为引用控制器没有意义,因为它很可能一直在 $scope.$parent 可用(但要注意设置范围的其他元素)。

不过,我会选择第一个解决方案。它添加了一些小标记,但解决了问题并保持了干净的分离。如果您使用第二个指令作为直接包装器,您还可以确定 $scope.$parent 与外部指令匹配。

于 2013-10-15T18:16:05.793 回答
20

& 局部范围属性允许指令的使用者传入指令可以调用的函数。

& 范围属性的说明

在此处查看详细信息。

这是一个类似问题的答案,它显示了如何从指令代码中传递回调函数中的参数。

于 2014-07-24T10:44:12.220 回答
8

在您的指令中要求ngController并将链接功能修改为:

ngGrid.directive("sortColumn", function() {
    return {
        ...
        require: "ngController",
        ...
        link: function(scope, element, attrs, ngCtrl) {
            ...
        }
    };
});

你得到的ngCtrl是你的控制器,GridCtrl. 你没有得到它的范围;你将不得不做一些事情:

xxxx.controller("GridCtrl", function($scope, ...) {
    // add stuff to scope as usual
    $scope.xxxx = yyyy;

    // Define controller public API
    // NOTE: USING this NOT $scope
    this.controllerOnSort = function(...) { ... };
});

简单地从链接函数调用它:

ngCtrl.controllerOnSort(...);

请注意,这 require 将获得第一个 parent ngController。如果在 和 指令之间指定了另一个控制器GridCtrl,您将得到那个控制器。

演示原理的小提琴(ng-controller使用方法访问父级的指令):http: //jsfiddle.net/NAfm5/1/


人们担心这种解决方案可能会引入不必要的紧耦合。如果这确实是一个问题,可以通过以下方式解决:

创建一个与控制器并行的指令,让我们调用它master

<table id="mainGrid" ng-controller="GridCtrl" master="controllerOnSort()">

该指令引用控制器的所需方法(因此:解耦)。

子指令(sort-column在您的情况下)需要master指令:

require: "^master"

使用该$parse服务,可以从主控制器的成员方法中调用指定的方法。请参阅实施此原则的更新小提琴:http: //jsfiddle.net/NAfm5/3/

于 2013-10-15T16:06:20.910 回答
2

还有另一种方法可以做到这一点,尽管鉴于我相对缺乏经验,我不能说这种解决方案的适用性。无论如何,我将传递它只是为了提供信息。

在您的专栏中,您创建一个范围变量属性:

<sort-column data-sortby="sortby">Date Created</sort-column>

然后在您的控制器中定义范围变量:

$scope.sortby = 'DateCreated' // just a default sort here

然后在控制器中添加您的排序功能:

$scope.onSort = function(val) {
    $scope.sortby = val;
}

然后在你的标记线上 ng-click:

<sort-column data-sortby="sortby" ng-click="onSort('DateCreated')">Date Created</sort-column>

然后在您的指令中将 sortby 属性添加到指令范围:

scope: {
    sortby: '=' // not sure if you need
}

并在您的“链接:”函数中添加一个 $watch:

scope.$watch('sortby', function () {
    ... your sort logic here ...
}

这种方法 IMO 的美妙之处在于您的指令完全解耦,您不需要从指令回调 onSort,因为在执行路径的那部分期间您并没有真正将 onSort 留在控制器中。

如果您需要告诉控制器等待排序完成,您可以在控制器中定义一个事件:

$scope.$on("_sortFinished", function(event, message){
   ..do something...  
});

然后在您的指令中简单地发出事件,然后该过程完成:

$scope.$emit('_sortFinished');

还有其他方法可以做到这一点,并且这种增加了一些紧密耦合,因为您的控制器必须监听。并且您的指令必须发出特定的偶数……但这对您来说可能不是问题,因为它们无论如何都密切相关。

于 2013-12-08T06:31:19.053 回答
1

叫我疯了,但通过内置方法从元素中获取控制器似乎更容易,而不是摆弄require

var mod = angular.module('something', []).directive('myDir', 
  function () {
    return {
      link: function (scope, element) {
        console.log(element.controller('myDir'));
      },
      controller: function () {
        this.works = function () {};
      },
      scope: {}
    }
  }
);

http://plnkr.co/edit/gY4rP0?p=preview

于 2015-12-01T13:17:52.620 回答