AngularJS指令通信没有相同的父级

时间:2013-03-24 17:24:52

标签: service angularjs directive concept

让我们想象我有一个包含一些数据的指令,我想在第二个组件(指令或其他)中将其插入其他地方,而不需要控制器链接它们。

例如,使用angularjs网页的zippy,但不是让<div ng-controller="Ctrl3">输入中的数据出价到 zippy指令,我们有两个单独的组件:

    <!-- first component with data -->
    <div ng-controller="aCtrl">

    Title: <input ng-model="title"> <br>
    Text: <textarea ng-model="text"></textarea>
    </div>

    <hr>
    <!-- a second component wanting to show the data -->
    <div class="zippy" zippy-title="Details: {{title}}...">{{text}}</div>

我的问题是如何在Angularjs中很好地链接它们?

我尝试通过服务,注册数据更改,然后尝试通过注入指令或通过控制器将其与我的DOM绑定失败。

(我想在另一个指令“window”中显示指令中包含的数据,但我不想用控制器包装我的所有代码只是为了绑定数据)

有没有办法很好地做到这一点?

2 个答案:

答案 0 :(得分:0)

这是一个使用服务触发$rootScope上的自定义事件的解决方案,然后在指令'

的控制器中侦听事件
app.factory('SharedData',function($rootScope){
    return{
        data:{text : 'World', title:'Foo bar'},
        upDate:function(prop,val){
           this.data[prop]=val;
           $rootScope.$emit('dataUpdate',prop)
        }
    }
});


app.controller('aCtrl', function ($scope,SharedData) {
    angular.forEach(SharedData.data,function(value,key){
        $scope[key] = value;
        $scope.$watch(key,function(){
            SharedData.upDate(key,$scope[key]);
        });
    });
});

app.directive('zippy',function($rootScope,SharedData){
    return{
        restrict:'C',
        replace:false,
        scope:{},
        controller:function($scope) {
            $rootScope.$on('dataUpdate',function(event,prop) {
                $scope[prop] = SharedData.data[prop];
            });
        }
    }
});

<强> Plunker Demo

答案 1 :(得分:0)

除了将事件或包装指令发送到父控制器之外还有另一种选择(btw这些选项没有错)。另一个选择是拥有一个通用服务/工厂,您可以在其中注册任意指令控制器,然后在其他相关或非相关指令中使用这些注册控制器。

下面有一个名为directiveCommunicator的服务,您可以在其中获取,设置和取消设置指令控制器(如果需要,可以使用工厂,只是我喜欢使用服务)。然后,我们有另外两个名为foobar的指令,foo指令注册要使用的控制器,而bar指令使用它们。请注意,foobar指令不是父/子相关的

// Service used to register/use any arbitrary controller
app.service('directiveCommunicator',
    function()
    {
        var _controllers = {};

        this.get =
            function(id)
            {
                if (!(id in _controllers)) {
                    return null;
                }

                return _controllers[id];
            };

        this.set =
            function(id, controller)
            {
                _controllers[id] = controller;
            };

        this.unset =
            function(id)
            {
                if (!(id in _controllers)) {
                    return;
                }

                delete _controllers[i];
            }
    }
);

app.directive('foo',
    [
        'directiveCommunicator',
        function(directiveCommunicator)
        {
            return {
                'restrict': 'A',
                'scope':
                    {
                        'colour': '='
                    },
                'controller':
                    function($scope)
                    {
                        // We register out controller with a unique ID so we can use it in other directives
                        directiveCommunicator.set('colourBox', this);

                        // We also unregister it once we get destroyed, otherwise we'll be leaking memory
                        $scope.$on('$destroy',
                            function()
                            {
                                directiveCommunicator.unset('colourBox');
                            }
                        );

                        this.changeColour =
                            function(colour)
                            {
                                $scope.$apply(
                                    function()
                                    {
                                        $scope._colour = colour;
                                    }
                                );
                            }
                    },
                'link':
                    function($scope, $element, $attr)
                    {
                        $scope._colour = $attr.colour;

                        $scope.$watch('_colour',
                            function()
                            {
                                $element.attr('class', $scope._colour);
                            }
                        );
                    }
            }
        }
    ]
);

app.directive('bar',
    [
        'directiveCommunicator',
        function(directiveCommunicator)
        {
            return {
                'restrict': 'A',
                'scope':
                    {
                        'colour': '='
                    },
                'link':
                    function($scope, $element, $attr)
                    {
                        $element.text($attr.colour);

                        $element.bind('click',
                            function()
                            {
                                // We get the registered controller and call the 'changeColour' method on it
                                var ctrl = directiveCommunicator.get('colourBox');

                                ctrl.changeColour($attr.colour);
                            }
                        );
                    }
            }
        }
    ]
);

我已经做了一些 Microsoft's source code 演示,看看foobar的实际效果。 foo指令只是一个小方形div,您可以在其中更改关联控制器中的背景。 bar是另一个非相关指令,它将调用foo的控制器方法changeColour并根据提供的属性更改颜色。

Haven尚未在生产中使用此设置,您还需要处理取消注册的控制器,但应该可以正常工作。您还可以在directiveCommunicator.set方法中将第3个参数作为控制器的范围,该方法将自动添加到$ destroy / unregister设置中,因此不必再调用directiveCommunicator.unset