Come richiedere un controller in una direttiva angularjs


86

Qualcuno può dirmi come includere un controller da una direttiva in un'altra direttiva angularJS. per esempio ho il seguente codice

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

Con tutti gli account dovrei essere in grado di accedere al controller nella direttiva addProduct ma non lo sono. C'è un modo migliore per farlo?


5
requireassicura la presenza di un'altra direttiva e quindi include il suo controller. ^requirecontrolla gli elementi sopra quello corrente oltre all'elemento corrente. Quindi devi usare le due direttive insieme affinché funzioni. Altrimenti, è sufficiente definire un controller con app.controllere quindi utilizzarlo in entrambe le direttive. Ad ogni modo, puoi metterlo in un semplice Plunker insieme al tuo codice HTML?
Josh David Miller

Risposte:


187

Sono stato fortunato e ho risposto in un commento alla domanda, ma sto postando una risposta completa per motivi di completezza e quindi possiamo contrassegnare questa domanda come "Risposta".


Dipende da cosa si vuole ottenere condividendo un controller; puoi condividere lo stesso controller (anche se hai istanze diverse) oppure puoi condividere la stessa istanza del controller.

Condividi un controller

Due direttive possono utilizzare lo stesso controller passando lo stesso metodo a due direttive, in questo modo:

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

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

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

Ciascuna direttiva otterrà la propria istanza del controller, ma ciò consente di condividere la logica tra tutti i componenti desiderati.

Richiedi un controller

Se desideri condividere la stessa istanza di un controller, utilizza require.

requireassicura la presenza di un'altra direttiva e quindi include il suo controller come parametro della funzione di collegamento. Quindi, se hai due direttive su un elemento, la tua direttiva può richiedere la presenza dell'altra direttiva e ottenere l'accesso ai suoi metodi di controllo. Un caso d'uso comune per questo è quello di require ngModel.

^require, con l'aggiunta del carattere di accento circonflesso, controlla gli elementi sopra la direttiva oltre all'elemento corrente per cercare di trovare l'altra direttiva. Ciò consente di creare componenti complessi in cui i "sottocomponenti" possono comunicare con il componente genitore attraverso il suo controller con grande effetto. Gli esempi potrebbero includere le schede, in cui ogni riquadro può comunicare con le schede generali per gestire la commutazione; un set di fisarmoniche potrebbe garantire che solo uno sia aperto alla volta; eccetera.

In entrambi i casi, è necessario utilizzare le due direttive insieme affinché funzioni. requireè un modo per comunicare tra i componenti.

Controlla la pagina della Guida delle direttive per maggiori informazioni: http://docs.angularjs.org/guide/directive


4
È possibile richiedere un controller di direttiva di pari livello? Fondamentalmente ho bisogno di condividere la stessa istanza di un controller o servizio tra le direttive di pari livello (come nei fratelli DOM, non sullo stesso elemento DOM) che viene ripetuta usando ng-repeat. Immagina che ogni elemento ripetuto abbia una direttiva che richiede uno stato o una logica condivisa tra di loro.
CMCDragonkai

2
@CMCDragonkai Non c'è modo di farlo, ma ci sono due modi comuni per ottenere la stessa cosa. Il primo è se i fratelli sono tutti dello stesso "tipo", l'elemento sopra ngRepeat può essere come una direttiva contenitore e tutti i sottoelementi possono quindi richiedere quella direttiva, condividendo tutti lo stesso controller. La soluzione più comune, e spesso più canonica, è utilizzare un servizio condiviso. Puoi approfondire cosa fanno questi fratelli e cosa devono condividere?
Josh David Miller

Sì, ho finito per fare la prima opzione. Utilizzo di un controller della direttiva contenitore. Funziona alla grande. È per la Massoneria.
CMCDragonkai

Questa è un'ottima risposta e ha consolidato la mia comprensione di come funziona tutto questo. Grazie! (Come nota, questa può essere una caratteristica più recente, ma è possibile utilizzare requireper specificare un'unica direttiva, o un array di direttive; ogni direttiva può essere preceduto da un accento circonflesso ( ^.) Per i requisiti più granulari)
jedd.ahyoung

L'uso dello stesso controller in due direttive non fornisce a ciascuna direttiva la propria istanza.
jsbisht

27

C'è una buona risposta di stackoverflow qui di Mark Rajcok:

Controller di direttiva AngularJS che richiedono controller di direttiva padre?

con un collegamento a questo jsFiddle molto chiaro: 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';
}

4
Per me, ciò che ha reso più cliccabile l'esempio di Mark Rajcok è stato prestare attenzione a come vengono creati i metodi del controller. In genere si vedono metodi del controller creati tramite $ scope.methodName = function () {...}, ma affinché funzioni, è necessario utilizzare this.methodName per i metodi che si desidera accedere. All'inizio non l'ho notato.
coblr
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.