调用Angular Directive的控制器功能

时间:2014-09-15 14:53:00

标签: angularjs angularjs-directive

我有一个图表指令:

.directive('chart', function() {
    return {
        ...
        controller: function($scope) {
            this.toggleAnimation = function() {
                ...
            };
        },
        link: function link(scope, element, attrs) {
            ...
        }
    }
});

我正在使用它:

<div ng-controller='foo'>
    <chart></chart>
</div>

foo的位置:

.controller('foo', function($scope) {
    // TODO: call chart's toggleAnimation
});

现在,如何从toggleAnimation控制器中调用chart指令上的foo函数?

或者这不是设置应该如何?我在这里要做的是为我的chart指令创建一个函数,允许任何消耗它的函数将指令中的变量转换为true / false。

3 个答案:

答案 0 :(得分:1)

.directive('chart', function() {
    return {
        scope: {
            toggle: "@" // pass as string - one way // you could also make this an attr if you want
        },
        ...
        controller: function($scope) {

        },
        link: function link(scope, element, attrs) {
            ...

            var toggleAnimation = function() {
                ...
            };

            // when you change this value, it will toggle the animation
            // logic which will check the values of this variable so you can do if statements and modify animations
            scope.$watch('toggle', function(newVal, oldVal){
                console.log(newVal);
                if(parseInt(newVal) === 1)
                    toggleAnimation();
                else if(parseInt(newVal) === 0)
                    ; // do something else like toggle back
            });
        }
    }
});

HTML

<div ng-controller='foo'>
    <chart toggle="myVariable"></chart>
</div>

Controller JS

.controller('foo', function($scope) {
    // TODO: call chart's toggleAnimation
    $scope.myVariable = 0;  // initialize to this value

    function clickSomething(){
        $scope.myVariable = 1;  // change, hence fire animation
    }


});

答案 1 :(得分:1)

有两种主要机制可以使数据在特定指令或控制器之间流动。数据可以使用范围和表达式向下范围层次结构(通常镜像DOM树),也可以使用指令控制器API以向上层次结构。这两种机制都需要一个指令与另一个特定指令进行通信。

第三种沟通机制是范围事件。这个机制是关于一个与零或更多其他指令/控制器进行通信的指令,它并不是必须知道的。

使用哪种机制取决于具体方案。以下部分概述了每个部分,然后概述了每个部分的权衡。 (在您给出的具体示例中,我使用了第一个,但您似乎对一般机制感兴趣,而不仅仅是您的具体示例。)


将数据向下传递树的惯用方法是提供图表访问其父作用域的数据。在这种情况下,它会像这样使用:

<div ng-controller="Foo">
    <chart animated="chartAnimated"></chart>
</div>

上面的chartAnimated是控制器插入的范围变量。以下是Foo控制器中的外观:

.controller('Foo', function($scope) {
    $scope.chartAnimated = true;
    $scope.toggleAnimation = function () {
        $scope.chartAnimated = ! $scope.chartAnimated;
    };
});

图表指令然后需要支持这个新属性,这可以使用指令声明中的scope属性来实现:

.directive('chart', function() {
    return {
        scope: {
            // This requests that Angular parse the expression in the 'animated'
            // attribute and write a function for it into the scope as
            // 'animationEnabled'.
            'animationEnabled': '&animated'
        },
        link: function link(scope, iElement, attrs) {
            // Now we can watch the expression to detect when it changes.
            scope.$watch(
                scope.animationEnabled,
                function (isEnabled) {
                    // This function will be called once on instantiation and then
                    // again each time the value of the expression changes.
                    // Use ``isEnabled`` in here to either enable or disable animation.
                    console.log('Animation', isEnabled ? 'is enabled' : 'is disabled');
                }
            );
        }
    }
});

虽然它并不适用于您给出的示例,但我们还要探索我提到的其他数据流技术,其中数据流 up 树。

在这种情况下,父指令可以将API公开给子指令。例如,ngModel指令与其父form指令的交互方式,或ngSwitchWhen与其父ngSwitch的交互方式。

这里的关键是指令声明中的require属性,它允许指令依赖于当前元素或某个父元素上的另一个指令。为了这个例子,我们将在任何父元素上查找它。

让我们做一个父母指令的一个人为设想的例子,它有许多孩子,由于某些原因需要跟踪:

<parent>
    <child name="foo"></child>
    <child name="bar"></child>
    <child name="baz"></child>
</parent>

我们首先定义parent指令:

.directive('parent', function() {
    return {
        controller: function () {
            this.children = {};
            this.registerChild(name, child) {
                console.log('Got registration for child', name);
                this.children[name] = child;
            }
        }
    }
});

child指令是我们可以使用require机制的地方:

.directive('child', function() {
    return {
        require: '^parent', // Must be nested inside a 'parent' directive
        link: function (scope, iElement, attrs, parentCtrl) {
            // Notice the extra 'parentCtrl' parameter above.

            // Provide an API for parent to interact with child.
            var child = {};
            child.doSomething = function () {
                console.log('Child', attrs.name, 'requested to do something');
            };

            parentCtrl.registerChild(attrs.name, child);
        }
    }
});

在这种情况下,我们在父级和子级之间建立双向通信通道,子级使用require启动通道,并将父级与子级通信的对象传递给父级。当使用require时,link会有一个额外的参数,为控制器提供所请求的指令。


最后,让我们谈谈事件。这些最适用于您有一个指令(或者实际上,拥有范围的任何其他代码)希望向正在收听的人广播特定通知的情况。例如,$route使用ng-view事件与$routeChangeSuccess(以及正在侦听的其他任何人)进行通信,ng-view通过$viewContentLoaded警告应用程序的其余部分scope.$on

事件观察者属于范围,事件在范围层次结构中上下传播。

如果您持有示波器,则可以使用scope.$on( '$viewContentLoaded', function () { console.log('view content loaded!'); } ); 监视可能通过的任何事件:

$emit

如果您要发送一个事件,您可以使用scope.$emit( 'somethingHappened' ); 向范围层次结构发送消息

$broadcast

...或者您可以使用scope.$broadcast( 'somethingHappened' ); 向范围层次结构发送向下消息:

$broadcast

在某些情况下,您希望将事件传递给整个应用程序,在这种情况下,您可以$rootScope上的$rootScope.$broadcast( 'somethingHappened' );

require

要记住事件的一个重要事项是它们是一个点对多点&#34;机制,也就是说许多不同的接收者可能会看到&#34;同样的消息。这使得事件成为两个特定参与者之间定向交流的相当差的机制,因此应该谨慎使用事件。


因此,概述了AngularJS中指令的三种数据流机制。每种方法都有不同的权衡:

  • 通过作用域将数据传递给子指令会使子指令与父指令分离,但需要父指令(或者,在您的情况下,控制器)来提供子节点所需的数据。
  • ngSwitch机制最适合用于提供需要多个强相关元素参与的模板构造,例如在ngSwitchWhen仅存在ngSwitch的情况下使用{{1}},没有它就毫无用处。
  • 事件最适用于广泛通知,其中发件人并不特别关心接收邮件的人,并且收件人不一定知道是谁发送邮件。在这种情况下,发件人收件人真正脱离了彼此,甚至可能 收件人。

答案 2 :(得分:0)

您可以使用$scope.$broadcast向指令中的子范围广播事件。该指令将监听该指令,然后在听到正确的事件时运行该方法:

$scope.$broadcast("toggleAnimation", this.textToBroadcast);

小提琴:

http://jsfiddle.net/smaye81/q2hbnL5b/3/