如何使用* parent's * ControllerAs从自定义指令中访问父控制器函数?

时间:2015-08-14 00:12:57

标签: angularjs angularjs-directive angularjs-scope

我需要构建一个转换指令,将自定义指令转换为html。

  • 输入如:<link text="hello world"></link>
  • 应输出到:<a class="someclass" ng-click="linkClicked('hello world')"></a>
应该在指令的父控制器上调用

linkClicked

如果我是负责持有'link'指令的html(使用隔离范围),那将是非常容易的,但我不是。这是一个 as-is 输入,我必须想办法继续这样做。

有很多关于如何使用指令的默认范围进行类似绑定的示例,但是我使用带控制器的John Papa's建议来编写我的控制器,但是不想在控制器上创建另一个实例在指令中。

这是我到目前为止所做的事情:

(function () {
    'use strict';

    angular
        .module('app')
        .directive('link', link);

    link.$inject = ['$compile'];

    function link($compile) {
        return {
            restrict: 'E',
            replace: true,
            template: '<a class="someclass"></a>',
            terminal: true,
            priority: 1000,
            link: function (scope, element, attributes) {
                element.removeAttr('link'); // Remove the attribute to avoid indefinite loop.

                element.attr('ng-click', 'linkClicked(\'' + attributes.text + '\')');

                $compile(element)(scope);
        },
    };
}
})();
    父控制器中的
  • $scope.linkClicked = function(text){...}
  • 指令中的
  • element.attr('ng-click', 'abc.linkClicked(..)')(父级的controllerAs为abc) - 也可以

问题是我不知道哪个控制器将使用我的指令,并且不能对其中的'abc'名称进行硬编码。

你有什么建议我应该做的?

1 个答案:

答案 0 :(得分:1)

从您的问题中很难理解您所面临的所有限制因素,但如果您获得的唯一HTML就是:

<link text="some text">

你需要生成对某些函数的调用,然后该函数必须是:

  1. 指令
  2. 传达指令
  3. #1 是有问题的,因为该指令的用户现在需要了解其内部。尽管如此,如果您假设函数名称为linkClicked(或者您想要调用的任何名称),那么它是可能的,并且您的指令的用户必须特别注意别名他真正需要的函数的别名(可以使用&#34; controllerAs&#34;也可以):

    <div ng-controller="FooCtrl as foo" ng-init="linkClicked = foo.actualFunctionOfFoo">
       ...
    
       <link text="some text">
    
       ...
    </div>
    
    app.directive("link", function($compile){
      return {
        transclude: "element", // remove the entire element
        link: function(scope, element, attrs, ctrl){
          var template = '<a class="someclass" ng-click="linkClicked(\'' + 
                          attrs.text +
                         '\')">link</a>';
    
          $compile(template)(scope, function(clone){
            element.after(clone);
          });
        }
      };
    });
    

    <强> Demo

    #2 通常是通过属性实现的,在您的情况下这是不可能的。但你也可以创建一种&#34;代理&#34;指令 - 让我们称之为onLinkClick - 它可以执行你需要的任何表达式:

    <div ng-controller="FooCtrl as foo"
         on-link-click="foo.actualFunctionOfFoo($data)">
       ...
    
       <link text="some text">
    
       ...
    </div>
    

    link指令现在需要require: "onLinkClick"

    app.directive("link", function($compile){
      return {
        transclude: "element", // remove the entire element
        scope: true,
        require: "?^onLinkClick",
        link: function(scope, element, attrs, ctrl){
          if (!ctrl) return;
          var template = '<a class="someclass" ng-click="localClick()">link</a>';
    
          scope.localClick = function(){
            ctrl.externalFn(attrs.text);
          };
    
          $compile(template)(scope, function(clone){
            element.after(clone);
          });
        }
      };
    });
    
    app.directive("onLinkClick", function($parse){
      return {
        restrict: "A",
        controller: function($scope, $attrs){
          var ctrl = this;
          var expr = $parse($attrs.onLinkClick);
    
          ctrl.externalFn = function(data){
            expr($scope, {$data: data});
          };
        },
      };
    });
    

    <强> Demo

    请注意,link指令也会在<link>内的<head>上执行。因此,尝试检测它并跳过所有内容。出于演示目的,我使用了一个名为blink的指令来避免此问题。