Comment exiger un contrôleur dans une directive angularjs

86

Quelqu'un peut-il me dire comment inclure un contrôleur d'une directive dans une autre directive angularJS. par exemple j'ai le code suivant

var app = angular.module('shop', []).
config(['$routeProvider', function ($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/js/partials/home.html'
    })
        .when('/products', {
        controller: 'ProductsController',
        templateUrl: '/js/partials/products.html'
    })
        .when('/products/:productId', {
        controller: 'ProductController',
        templateUrl: '/js/partials/product.html'
    });
}]);

app.directive('mainCtrl', function () {
    return {
        controller: function ($scope) {}
    };
});

app.directive('addProduct', function () {
    return {
        restrict: 'C',
        require: '^mainCtrl',
        link: function (scope, lElement, attrs, mainCtrl) {
            //console.log(cartController);
        }
    };
});

De toute évidence, je devrais pouvoir accéder au contrôleur dans la directive addProduct mais je ne le suis pas. Existe-t-il une meilleure façon de le faire?

Le Garden Fox
la source
5
requireassure la présence d'une autre directive et inclut alors son contrôleur. ^requirevérifie les éléments au-dessus de l'élément actuel en plus de l'élément actuel. Vous devez donc utiliser les deux directives ensemble pour que cela fonctionne. Sinon, définissez simplement un contrôleur avec app.controller, puis utilisez-le dans les deux directives. Quoi qu'il en soit, pouvez-vous mettre cela dans un simple Plunker avec votre code HTML?
Josh David Miller

Réponses:

187

J'ai eu de la chance et j'ai répondu à cela dans un commentaire à la question, mais je publie une réponse complète par souci d'exhaustivité et nous pouvons donc marquer cette question comme "Répondue".


Cela dépend de ce que vous voulez accomplir en partageant un contrôleur; vous pouvez soit partager le même contrôleur (mais avoir des instances différentes), soit partager la même instance de contrôleur.

Partager un contrôleur

Deux directives peuvent utiliser le même contrôleur en passant la même méthode à deux directives, comme ceci:

app.controller( 'MyCtrl', function ( $scope ) {
  // do stuff...
});

app.directive( 'directiveOne', function () {
  return {
    controller: 'MyCtrl'
  };
});

app.directive( 'directiveTwo', function () {
  return {
    controller: 'MyCtrl'
  };
});

Chaque directive obtiendra sa propre instance du contrôleur, mais cela vous permet de partager la logique entre autant de composants que vous le souhaitez.

Exiger un contrôleur

Si vous souhaitez partager la même instance d'un contrôleur, vous utilisez require.

requireassure la présence d'une autre directive et inclut ensuite son contrôleur comme paramètre de la fonction de liaison. Donc, si vous avez deux directives sur un élément, votre directive peut exiger la présence de l'autre directive et accéder à ses méthodes de contrôleur. Un cas d'utilisation courant pour cela est d'exigerngModel .

^require, avec l'ajout du curseur, vérifie les éléments au-dessus de la directive en plus de l'élément actuel pour essayer de trouver l'autre directive. Cela vous permet de créer des composants complexes dans lesquels les «sous-composants» peuvent communiquer avec le composant parent via son contrôleur de manière efficace. Les exemples peuvent inclure des onglets, où chaque volet peut communiquer avec les onglets globaux pour gérer la commutation; un ensemble d'accordéon pourrait garantir qu'un seul est ouvert à la fois; etc.

Dans les deux cas, vous devez utiliser les deux directives ensemble pour que cela fonctionne. requireest une manière de communiquer entre les composants.

Consultez la page Guide des directives pour plus d'informations: http://docs.angularjs.org/guide/directive

Josh David Miller
la source
4
Est-il possible d'exiger un contrôleur de directive frère? Fondamentalement, j'ai besoin de partager la même instance d'un contrôleur ou d'un service entre des directives frères (comme dans les frères DOM, pas sur le même élément DOM) qui est répétée en utilisant ng-repeat. Imaginez que chaque élément répété a une directive qui nécessite un état ou une logique partagé entre eux.
CMCDragonkai
2
@CMCDragonkai Il n'y a aucun moyen de faire cela, mais il existe deux façons courantes d'accomplir la même chose. La première est que si les frères et sœurs sont tous du même «type», alors l'élément au-dessus du ngRepeat peut être comme une directive de conteneur et tous les sous-éléments peuvent alors exiger cette directive à la place, tous partageant le même contrôleur. La solution la plus courante - et souvent plus canonique - consiste à utiliser un service partagé. Pouvez-vous expliquer ce que font ces frères et sœurs et ce qu'ils doivent partager?
Josh David Miller
Oui, j'ai fini par faire la première option. Utilisation d'un contrôleur de directive de conteneur. Fonctionne très bien. C'est pour la maçonnerie.
CMCDragonkai
C'est une excellente réponse et a renforcé ma compréhension de la façon dont tout cela fonctionne. Merci! (À noter, cela peut être une fonctionnalité plus récente, mais vous pouvez l'utiliser requirepour spécifier une seule directive ou un tableau de directives; chaque directive peut être précédée d'un signe caret ( ^) pour des exigences plus granulaires.)
jedd.ahyoung
L'utilisation du même contrôleur dans deux directives ne donne pas à chaque directive sa propre instance.
jsbisht
27

Il y a une bonne réponse stackoverflow ici par Mark Rajcok:

Contrôleurs de directive AngularJS nécessitant des contrôleurs de directive parent?

avec un lien vers ce jsFiddle très clair: http://jsfiddle.net/mrajcok/StXFK/

<div ng-controller="MyCtrl">
    <div screen>
        <div component>
            <div widget>
                <button ng-click="widgetIt()">Woo Hoo</button>
            </div>
        </div>
    </div>
</div>

JavaScript

var myApp = angular.module('myApp',[])

.directive('screen', function() {
    return {
        scope: true,
        controller: function() {
            this.doSomethingScreeny = function() {
                alert("screeny!");
            }
        }
    }
})

.directive('component', function() {
    return {
        scope: true,
        require: '^screen',
        controller: function($scope) {
            this.componentFunction = function() {
                $scope.screenCtrl.doSomethingScreeny();
            }
        },
        link: function(scope, element, attrs, screenCtrl) {
            scope.screenCtrl = screenCtrl
        }
    }
})

.directive('widget', function() {
    return {
        scope: true,
        require: "^component",
        link: function(scope, element, attrs, componentCtrl) {
            scope.widgetIt = function() {
                componentCtrl.componentFunction();
            };
        }
    }
})


//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

function MyCtrl($scope) {
    $scope.name = 'Superhero';
}
Joseph Oster
la source
4
Pour moi, ce qui a le plus motivé l'exemple de Mark Rajcok, c'est de prêter attention à la manière dont les méthodes de contrôleur sont créées. En général, vous voyez des méthodes de contrôleur créées via $ scope.methodName = function () {...}, mais pour que cela fonctionne, vous devez utiliser this.methodName pour les méthodes que vous souhaitez accéder. Je n'ai pas remarqué cela au début.
coblr