无法找到指令所需的控制器

时间:2014-01-24 21:22:14

标签: angularjs angularjs-directive angularjs-scope

我有一个指令,我希望能够调用另一个指令。我一直在尝试使用指令控制器来实现这一点。

指令1将与指令2位于同一页面上,指令1将调用指令2的控制器公开的方法:

指令1:

'use strict';
angular.module('angularTestApp')
    .directive('fileLibrary', function () {
        return {
            templateUrl: 'views/manage/file_library/file-library.html',
            require: 'videoClipDetails',
            restrict: 'AE',
            link: function postLink(scope, element, attrs, videClipDetailsCtrl) {
                scope.doSomethingInVideoClipDirective = function() {
                    videClipDetailsCtrl.doSomething();
                }
            }
        };
    });

指令二:

'use strict';
angular.module('angularTestApp')
    .directive('videoClipDetails', function () {
        return {
            templateUrl: 'views/video_clip/video-clip-details.html',
            restrict: 'AE',
            controller: function($scope, $element) {
                this.doSomething = function() {
                    console.log('I did something');
                }
            },
            link: function postLink(scope, element, attrs) {
                console.log('videoClipDetails directive');
                //start the element out as hidden
            }
        };
    });

使用两者并将其设置为兄弟姐妹的文件:

<div>
    <div video-clip-details></div>
    <!-- main component for the file library -->
    <div file-library></div>
</div>

我知道阅读文档,我知道当指令在同一个元素上时控制器可以共享,这让我觉得我可能会以错误的方式看待这个问题。谁能让我走上正轨?

2 个答案:

答案 0 :(得分:16)

来自angular.js documentation on directives

  

当指令使用require时,$compile将抛出错误,除非找到指定的控制器。 ^前缀意味着该指令在其父节点上搜索控制器(没有^前缀,该指令将仅在其自己的元素上查找控制器。)

所以基本上你想要让兄弟姐妹直接沟通是不可能的。我遇到了同样的问题,但我不想使用服务进行通信。我想出的是一种使用父指令来管理其子节点之间的通信的方法,这些子节点是兄弟节点。我发布了the example on github

两个孩子都需要父母(require: '^parentDirective')和他们自己的控制器,两者都被传递到链接功能。从那里,每个孩子都可以获得对父控制器及其所有公共方法的引用,作为各种API。

以下是其中一个孩子itemEditor

function itemEditor() {
    var directive = {
        link: link,
        scope: {},
        controller: controller,
        controllerAs: 'vm',
        require: ['^itemManager', 'itemEditor'],
        templateUrl: 'app/scripts/itemManager/itemManager.directives.itemEditor.html',
        restrict: 'A'
    };

    return directive;

    function link(scope, element, attrs, controllers) {
        var itemManagerController = controllers[0];
        var itemEditorController = controllers[1];

        itemEditorController.itemManager = itemManagerController;

        itemEditorController.initialize();
    }

    function controller() {
        var vm = this;

        // Properties
        vm.itemManager = {};
        vm.item = { id: -1, name: "", size: "" };

        // Methods
        vm.initialize = initialize;
        vm.updateItem = updateItem;
        vm.editItem = editItem;

        // Functions
        function initialize() {
            vm.itemManager.respondToEditsWith(vm.editItem);
        }

        function updateItem() {
            vm.itemManager.updateItem(vm.item);
            vm.item = {};
        }

        function editItem(item) {
            vm.item.id = item.id;
            vm.item.name = item.name;
            vm.item.size = item.size;
        }
    }
}

请注意传递到require数组的值是父指令的名称和当前指令的名称。然后,可以通过link参数在controllers函数中访问它们。将父指令的控制器指定为当前子节点的属性,然后可以通过该属性在子控制器函数内访问它。

还要注意子指令的link函数如何从孩子的控制器中调用initialize函数。这是通信线路的一部分建立的地方。

我基本上说,只要您(父指令)收到编辑项目的请求,请使用我的名为editItem的方法,该方法将item作为参数。

这是父指令

function itemManager() {
    var directive = {
        link: link,
        controller: controller,
        controllerAs: 'vm',
        templateUrl: 'app/scripts/itemManager/itemManager.directives.itemManager.html',
        restrict: 'A'
    };

    return directive;

    function link(scope, element, attrs, controller) {

    }

    function controller() {
        var vm = this;

        vm.updateMethod = null;
        vm.editMethod = null;

        vm.updateItem = updateItem;
        vm.editItem = editItem;
        vm.respondToUpdatesWith = respondToUpdatesWith;
        vm.respondToEditsWith = respondToEditsWith;

        function updateItem(item) {
            vm.updateMethod(item);
        }

        function editItem(item) {
            vm.editMethod(item);
        }

        function respondToUpdatesWith(method) {
            vm.updateMethod = method;
        }

        function respondToEditsWith(method) {
            vm.editMethod = method;
        }
    }
}

在父级中,您可以看到respondToEditsWith将方法作为参数,并将该值分配给其editMethod属性。只要调用控制器的editItem方法并将item对象传递给它,就会调用此属性,从而调用子指令的editItem方法。同样,保存数据的方式也相反。

更新:顺便说一下,这里是a blog post on coderwall.com我在这里得到了最初的想法,并在指令中提供了require和控制器选项的良好示例。也就是说,他对该帖子中最后一个例子的推荐语法对我不起作用,这就是我创建上面引用的例子的原因。

答案 1 :(得分:7)

没有真正的方法要求以您在此处尝试的方式在兄弟元素之间进行通信。如果两个指令在相同的元素上,则需要按照您设置的方式工作。

但是你不能这样做,因为你的两个指令都有一个你想要使用的关联templateUrl,每个元素只能有一个。

您可以稍微改变一下您的html结构,以便允许它工作。你基本上需要将一个指令放在另一个指令(transcluded)中并使用require: '^videoClipDetails'。这意味着它将寻找父母找到它。 我已经设置了一个小提琴来证明这一点:http://jsfiddle.net/WwCvQ/1/

这是让父母工作的代码:

// In videoClipDetails
template: '<div>clip details<div ng-transclude></div></div>',
transclude: 'true',
...
// in markup
<div video-clip-details> 
    <div file-library></div>
</div>
// in fileLibrary
require: '^videoClipDetails',

如果您有任何疑问,请与我联系!