Як вимагати контролера в директиві angularjs


86

Хтось може сказати мені, як включити контролер з однієї директиви в іншу директиву angularJS. наприклад, у мене є такий код

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

З іншого боку, я мав би мати доступ до контролера в директиві addProduct, але не маю. Чи є кращий спосіб зробити це?


5
requireзабезпечує наявність іншої директиви, а потім включає її контролер. ^requireперевіряє елементи над поточним на додаток до поточного елемента. Отже, вам потрібно використовувати дві директиви разом, щоб це працювало. В іншому випадку просто визначте контролер за допомогою, app.controllerа потім використовуйте його в обох директивах. У будь-якому випадку, чи можете ви помістити це у простий Plunker разом зі своїм HTML-кодом?
Джош Девід Міллер

Відповіді:


187

Мені пощастило і я відповів на це в коментарі до запитання, але для повноти публікую повну відповідь, і ми можемо позначити це питання як "Відповідь".


Це залежить від того, що ви хочете досягти спільним використанням контролера; Ви можете або спільно використовувати один і той же контролер (хоча мають різні екземпляри), або ви можете спільно використовувати той самий екземпляр контролера.

Поділіться контролером

Дві директиви можуть використовувати один і той же контролер, передаючи один і той же метод двом директивам, наприклад:

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

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

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

Кожна директива отримає власний екземпляр контролера, але це дозволяє розподіляти логіку між скільки завгодно компонентів.

Потрібен контролер

Якщо ви хочете поділитися одним і тим же екземпляром контролера, тоді ви використовуєте require.

requireзабезпечує наявність іншої директиви, а потім включає її контролер як параметр до функції зв'язку. Отже, якщо у вас є дві директиви щодо одного елемента, ваша директива може вимагати наявності іншої директиви та отримати доступ до методів контролера. Поширеним випадком використання для цього є вимагати ngModel.

^require, з додаванням каретки, перевіряє елементи вище директиви на додаток до поточного елемента, щоб спробувати знайти іншу директиву. Це дозволяє створювати складні компоненти, де "підкомпоненти" можуть ефективно взаємодіяти з батьківським компонентом через його контролер. Прикладами можуть бути вкладки, де кожна панель може взаємодіяти із загальними вкладками для обробки перемикань; набір акордеонів міг забезпечити одночасне відкриття лише одного; тощо

У будь-якому випадку вам доведеться використовувати дві директиви разом, щоб це працювало. require- це спосіб спілкування між компонентами.

Ознайомтесь зі сторінкою керівництва з директив для отримання додаткової інформації: http://docs.angularjs.org/guide/directive


4
Чи можна вимагати контролера директив братів або сестер? В основному мені потрібно спільно використовувати той самий екземпляр контролера або служби між директивами братів і сестер (як у братів і сестер DOM, а не в одному елементі DOM), який повторюється за допомогою ng-repeat. Уявіть, що кожен повторюваний елемент має директиву, яка потребує спільного стану або логіки між ними.
CMCDragonkai

2
@CMCDragonkai Неможливо це зробити, але є два загальних способи досягнення одного і того ж. По-перше, якщо всі брати і сестри однакового "типу", тоді елемент над ngRepeat може бути схожий на директиву контейнера, і тоді всі суб-елементи можуть вимагати цю директиву, натомість усі мають один і той же контролер. Найпоширенішим рішенням - і часто більш канонічним - є використання спільної послуги. Чи можете ви детальніше розповісти про те, що роблять ці брати і сестри, і що їм потрібно поділитися?
Джош Девід Міллер

Так в кінцевому підсумку зробив перший варіант. Використання контролера директиви контейнера. Чудово працює. Це для масонства.
CMCDragonkai

Це чудова відповідь і закріпило моє розуміння того, як все це працює. Дякую! (Як примітка, це може бути новішою функцією, але ви можете використовувати requireдля вказівки одну директиву або масив директив; кожна директива може мати префікс з символом ( ^) для більш
детальних

Використання одного і того ж контролера у двох директивах не надає кожній директиві власний екземпляр.
jsbisht

27

Тут є хороша відповідь на stackoverflow від Марка Райкока:

Для контролерів директив AngularJS потрібні батьківські контролери директив?

із посиланням на цей дуже зрозумілий 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';
}

4
Для мене те, що найбільше натиснуло приклад Марка Райкока, було звернення уваги на те, як створюються методи контролера. Зазвичай ви бачите методи контролера, створені за допомогою $ scope.methodName = function () {...}, але для того, щоб це працювало, ви повинні використовувати this.methodName для методів, до яких ви хочете отримати доступ. Я спочатку цього не помітив.
coblr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.