Wie man einen Controller in einer AngularJS-Direktive benötigt

86

Kann mir jemand sagen, wie man einen Controller aus einer Direktive in eine andere angleJS-Direktive einbindet. Zum Beispiel habe ich den folgenden Code

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

Auf jeden Fall sollte ich in der addProduct-Direktive auf den Controller zugreifen können, bin es aber nicht. Gibt es einen besseren Weg, dies zu tun?

Edgar Martinez
quelle
5
requirestellt das Vorhandensein einer anderen Richtlinie sicher und schließt dann deren Controller ein. ^requireprüft zusätzlich zum aktuellen Element Elemente über dem aktuellen. Sie müssen also die beiden Anweisungen zusammen verwenden, damit dies funktioniert. Andernfalls definieren Sie einfach einen Controller mit app.controllerund verwenden Sie ihn dann in beiden Anweisungen. Wie auch immer, können Sie dies zusammen mit Ihrem HTML-Code in einen einfachen Plunker einfügen?
Josh David Miller

Antworten:

187

Ich hatte Glück und beantwortete dies in einem Kommentar zu der Frage, aber der Vollständigkeit halber poste ich eine vollständige Antwort, damit wir diese Frage als "Beantwortet" markieren können.


Dies hängt davon ab, was Sie durch die gemeinsame Nutzung eines Controllers erreichen möchten. Sie können entweder denselben Controller gemeinsam nutzen (obwohl Sie unterschiedliche Instanzen haben), oder Sie können dieselbe Controller-Instanz gemeinsam nutzen.

Teilen Sie einen Controller

Zwei Direktiven können denselben Controller verwenden, indem sie dieselbe Methode an zwei Direktiven übergeben:

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

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

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

Jede Direktive erhält eine eigene Instanz des Controllers. Auf diese Weise können Sie die Logik jedoch auf beliebig viele Komponenten verteilen.

Benötigen Sie einen Controller

Wenn Sie dieselbe Instanz eines Controllers gemeinsam nutzen möchten , verwenden Sie require.

requirestellt das Vorhandensein einer anderen Direktive sicher und schließt dann deren Controller als Parameter für die Verbindungsfunktion ein. Wenn Sie also zwei Direktiven für ein Element haben, kann Ihre Direktive das Vorhandensein der anderen Direktive erfordern und Zugriff auf ihre Controller-Methoden erhalten. Ein häufiger Anwendungsfall hierfür ist das Erfordernis ngModel.

^requireÜberprüft mit dem Caret zusätzlich zum aktuellen Element die Elemente über der Direktive, um zu versuchen, die andere Direktive zu finden. Auf diese Weise können Sie komplexe Komponenten erstellen, bei denen "Unterkomponenten" über die Steuerung mit der übergeordneten Komponente effektiv kommunizieren können. Beispiele könnten Registerkarten sein, in denen jeder Bereich mit den gesamten Registerkarten kommunizieren kann, um das Umschalten zu handhaben. Ein Akkordeonset könnte sicherstellen, dass jeweils nur eines geöffnet ist. etc.

In beiden Fällen müssen Sie die beiden Anweisungen zusammen verwenden, damit dies funktioniert. requireist eine Art der Kommunikation zwischen Komponenten.

Weitere Informationen finden Sie auf der Leitfadenseite der Richtlinien: http://docs.angularjs.org/guide/directive

Josh David Miller
quelle
4
Ist es möglich, einen Geschwister-Direktiven-Controller zu benötigen? Grundsätzlich muss ich dieselbe Instanz eines Controllers oder Dienstes zwischen Geschwisteranweisungen (wie bei DOM-Geschwistern, nicht auf demselben DOM-Element) gemeinsam nutzen, die mit ng-repeat wiederholt wird. Stellen Sie sich vor, jedes wiederholte Element hat eine Direktive, die einen gemeinsamen Status oder eine gemeinsame Logik zwischen ihnen benötigt.
CMCDragonkai
2
@CMCDragonkai Es gibt keine Möglichkeit, dies zu tun, aber es gibt zwei gängige Methoden, um dasselbe zu erreichen. Das erste ist, wenn die Geschwister alle vom gleichen "Typ" sind, kann das Element über dem ngRepeat wie eine Container-Direktive sein, und alle Unterelemente können stattdessen diese Direktive erfordern, wobei alle denselben Controller gemeinsam nutzen. Die üblichere - und oft kanonischere - Lösung ist die Verwendung eines gemeinsam genutzten Dienstes. Können Sie näher erläutern, was diese Geschwister tun und was sie teilen müssen?
Josh David Miller
Yep hat am Ende die erste Option gemacht. Verwenden eines Container-Direktiven-Controllers. Funktioniert super. Es ist für Mauerwerk.
CMCDragonkai
Dies ist eine großartige Antwort und hat mein Verständnis dafür, wie all dies funktioniert, gefestigt. Vielen Dank! (Als Hinweis kann dies eine neuere Funktion sein, aber Sie können requireeine einzelne Direktive oder ein Array von Direktiven angeben. Jeder Direktive kann ein Caret ( ^) vorangestellt werden, um genauere Anforderungen zu erfüllen.)
jedd.ahyoung
Die Verwendung desselben Controllers in zwei Direktiven gibt nicht jeder Direktive eine eigene Instanz.
Jsbisht
27

Hier gibt es eine gute Stackoverflow-Antwort von Mark Rajcok:

AngularJS-Direktiven-Controller, die übergeordnete Direktiven-Controller benötigen?

mit einem Link zu dieser sehr klaren jsFiddle: 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
quelle
4
Für mich war das, was Mark Rajcoks Beispiel am meisten zum Klicken brachte, die Aufmerksamkeit darauf, wie die Controller-Methoden erstellt werden. Normalerweise werden Controller-Methoden angezeigt, die über $ scope.methodName = function () {...} erstellt wurden. Damit dies jedoch funktioniert, müssen Sie this.methodName für die Methoden verwenden, auf die zugegriffen werden soll. Das habe ich zuerst nicht bemerkt.
Coblr