Jak wymagać kontrolera w dyrektywie angularjs

86

Czy ktoś może mi powiedzieć, jak włączyć kontroler z jednej dyrektywy do innej dyrektywy angularJS. na przykład mam następujący kod

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

Pod każdym względem powinienem mieć dostęp do kontrolera w dyrektywie addProduct, ale nie mam. Czy jest lepszy sposób na zrobienie tego?

Le Garden Fox
źródło
5
requirezapewnia obecność innej dyrektywy, a następnie dołącza jej kontrolera. ^requiresprawdza elementy powyżej bieżącego oprócz bieżącego elementu. Aby to zadziałało, musisz użyć obu dyrektyw razem. W przeciwnym razie po prostu zdefiniuj kontroler za pomocą, app.controllera następnie użyj go w obu dyrektywach. Tak czy inaczej, czy możesz umieścić to w prostym Plunkerze wraz z kodem HTML?
Josh David Miller,

Odpowiedzi:

187

Miałem szczęście i odpowiedziałem na to w komentarzu do pytania, ale zamieszczam pełną odpowiedź ze względu na kompletność, abyśmy mogli oznaczyć to pytanie jako „Z odpowiedzią”.


To zależy od tego, co chcesz osiągnąć, udostępniając kontroler; możesz współużytkować ten sam kontroler (choć mają różne instancje) lub tę samą instancję kontrolera.

Udostępnij kontroler

Dwie dyrektywy mogą używać tego samego kontrolera, przekazując tę ​​samą metodę do dwóch dyrektyw, na przykład:

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

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

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

Każda dyrektywa otrzyma własną instancję kontrolera, ale umożliwia to współdzielenie logiki między dowolną liczbą komponentów.

Wymagaj kontrolera

Jeśli chcesz współużytkować tę samą instancję kontrolera, użyj require.

requirezapewnia obecność innej dyrektywy, a następnie dołącza jej kontroler jako parametr do funkcji łącza. Więc jeśli masz dwie dyrektywy dla jednego elementu, twoja dyrektywa może wymagać obecności drugiej dyrektywy i uzyskać dostęp do jej metod kontrolera. Typowym przypadkiem użycia jest wymaganie ngModel.

^require, po dodaniu daszka, sprawdza elementy powyżej dyrektywy oprócz bieżącego elementu, aby spróbować znaleźć inną dyrektywę. Pozwala to na tworzenie złożonych komponentów, w których „podkomponenty” mogą komunikować się z komponentem nadrzędnym za pośrednictwem swojego kontrolera z doskonałym efektem. Przykłady mogą obejmować karty, w których każdy panel może komunikować się z wszystkimi kartami w celu obsługi przełączania; zestaw akordeonowy mógłby zapewnić, że w danym momencie otwarty jest tylko jeden; itp.

W obu przypadkach musisz użyć obu dyrektyw razem, aby to zadziałało. requireto sposób komunikacji między komponentami.

Więcej informacji znajdziesz na stronie Przewodnik po dyrektywach: http://docs.angularjs.org/guide/directive

Josh David Miller
źródło
4
Czy można wymagać kontrolera z dyrektywą siostrzaną? Zasadniczo muszę udostępniać tę samą instancję kontrolera lub usługi między dyrektywami rodzeństwa (jak w przypadku rodzeństwa DOM, a nie w tym samym elemencie DOM), która jest powtarzana za pomocą ng-repeat. Wyobraź sobie, że każdy powtarzający się element ma dyrektywę, która wymaga wspólnego stanu lub logiki między nimi.
CMCDragonkai
2
@CMCDragonkai Nie ma sposobu, aby to zrobić, ale są dwa typowe sposoby osiągnięcia tego samego. Po pierwsze, jeśli rodzeństwo jest tego samego „typu”, wówczas element powyżej ngRepeat może być jak dyrektywa kontenera, a wszystkie elementy podrzędne mogą wymagać tej dyrektywy, a wszystkie współdzielą ten sam kontroler. Bardziej powszechnym rozwiązaniem - i często bardziej kanonicznym - jest skorzystanie ze wspólnej usługi. Czy możesz wyjaśnić, co robi to rodzeństwo i czym musi się podzielić?
Josh David Miller
Tak skończyło się na pierwszej opcji. Korzystanie z kontrolera dyrektywy kontenera. Działa świetnie. To dla Masonry.
CMCDragonkai
To świetna odpowiedź i ugruntowała moje zrozumienie tego, jak to wszystko działa. Dzięki! (Uwaga: może to być nowsza funkcja, ale można jej użyć requiredo określenia jednej dyrektywy lub tablicy dyrektyw; każda dyrektywa może być poprzedzona znakiem daszka ( ^), aby uzyskać bardziej szczegółowe wymagania).
jedd.ahyoung
Użycie tego samego kontrolera w dwóch dyrektywach nie daje każdej dyrektywie jej własnego wystąpienia.
jsbisht
27

Jest tutaj dobra odpowiedź na temat przepełnienia stosu autorstwa Marka Rajcoka:

Kontrolery dyrektywy AngularJS wymagają kontrolerów dyrektywy nadrzędnej?

z linkiem do tego bardzo przejrzystego 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
źródło
4
Dla mnie to, co sprawiło, że przykład Marka Rajcoka kliknął najbardziej, to zwrócenie uwagi na sposób tworzenia metod kontrolera. Zwykle widzisz metody kontrolera utworzone za pomocą $ scope.methodName = function () {...}, ale aby to zadziałało, musisz użyć this.methodName dla metod, które chcesz udostępnić. Na początku tego nie zauważyłem.
coblr