web-dev-qa-db-fra.com

Contrôleur requis par la directive introuvable

J'ai une directive que j'aimerais qu'une autre directive puisse appeler. J'ai essayé d'utiliser des contrôleurs directifs pour y parvenir.

La directive un serait assise sur la même page que la directive deux, et la directive un appellerait les méthodes exposées par le contrôleur de la directive deux:

Directive 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();
                }
            }
        };
    });

Directive deux:

'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
            }
        };
    });

Fichier où les deux sont utilisés et configurés en tant que frères et sœurs:

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

Je sais que la lecture de la documentation que j'ai prise que les contrôleurs peuvent être partagés lorsque les directives sont sur le même élément, ce qui me fait penser que je pourrais regarder ce problème dans le mauvais sens. Quelqu'un peut-il me mettre sur la bonne voie?

23
binarygiant

Depuis angular.js documentation sur les directives

Lorsqu'une directive utilise require, $compile générera une erreur sauf si le contrôleur spécifié est trouvé. Le ^ le préfixe signifie que cette directive recherche le contrôleur sur ses parents (sans le ^ préfixe, la directive ne recherchera le contrôleur que sur son propre élément).

Donc, fondamentalement, ce que vous essayez de faire pour que les frères et sœurs communiquent directement n'est pas possible. J'avais rencontré ce même problème mais je ne voulais pas utiliser un service de communication. J'ai trouvé une méthode d'utilisation d'une directive parent pour gérer la communication entre ses enfants, qui sont des frères et sœurs. J'ai posté l'exemple sur github .

Ce qui se passe, c'est que les deux enfants ont besoin du parent (require: '^parentDirective') et leur propre contrôleur, tous deux transmis à la fonction de liaison. À partir de là, chaque enfant peut obtenir une référence au contrôleur parent et à toutes ses méthodes publiques, comme une sorte d'API.

Ci-dessous, l'un des enfants 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;
        }
    }
}

Notez que les valeurs passées dans le tableau require sont le nom de la directive parent et le nom de la directive actuelle. Celles-ci sont alors toutes deux accessibles dans la fonction link via le paramètre controllers. Attribuez le contrôleur de la directive parent en tant que propriété de l'enfant actuel, puis il est accessible dans les fonctions de contrôleur de l'enfant via cette propriété.

Notez également comment dans la fonction link de la directive enfant j'appelle une fonction initialize depuis le contrôleur de l'enfant. C'est là qu'une partie des lignes de communication est établie.

Je dis essentiellement que chaque fois que vous (directive parent) recevez une demande de modification d'un élément, utilisez cette méthode nommée editItem qui prend un item comme paramètre.

Voici la directive parent

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;
        }
    }
}

Ici, dans le parent, vous pouvez voir que respondToEditsWith prend une méthode comme paramètre et assigne cette valeur à sa propriété editMethod. Cette propriété est appelée chaque fois que la méthode editItem du contrôleur est appelée et que l'objet item lui est transmis, appelant ainsi la méthode editItem de la directive enfant. De même, l'enregistrement des données fonctionne de la même manière en sens inverse.

Mise à jour : Au fait, voici n article de blog sur coderwall.com où j'ai eu l'idée originale avec de bons exemples de require et options de contrôleur dans les directives. Cela dit, sa syntaxe recommandée pour le dernier exemple de ce message n'a pas fonctionné pour moi, c'est pourquoi j'ai créé l'exemple que je référence ci-dessus.

16
seangwright

Il n'y a aucun moyen réel d'exiger de communiquer entre les éléments frères de la manière que vous essayez de faire ici. Le require fonctionne comme vous l'avez configuré si les deux directives sont sur le même élément .

Cependant, vous ne pouvez pas le faire car vos deux directives ont un templateUrl associé que vous souhaitez utiliser, et vous ne pouvez en avoir qu'un par élément.

Vous pouvez cependant structurer votre html légèrement différemment pour que cela fonctionne. Vous devez essentiellement mettre une directive à l'intérieur de l'autre (transclus) et utiliser require: '^videoClipDetails'. Cela signifie qu'il cherchera le parent pour le trouver. J'ai mis en place un violon pour le démontrer: http://jsfiddle.net/WwCvQ/1/

C'est le code qui fait fonctionner la chose parent:

// 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',

faites moi savoir si vous avez des questions!

7
hassassin