Как требовать контроллер в директиве 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, но это не так. Есть ли лучший способ сделать это?

Le Garden Fox
источник
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 08
2
@CMCDragonkai Нет способа сделать это, но есть два распространенных способа сделать то же самое. Во-первых, если все братья и сестры имеют один и тот же «тип», тогда элемент над ngRepeat может быть похож на директиву контейнера, и все подэлементы могут вместо этого потребовать эту директиву, используя один и тот же контроллер. Более распространенное решение - и часто более каноническое - использовать общий сервис. Не могли бы вы рассказать о том, чем занимаются эти братья и сестры и чем им нужно поделиться?
Джош Дэвид Миллер
Ага в итоге выбрал первый вариант. Использование контроллера директивы контейнера. Работает отлично. Это для масонства.
CMCDragonkai 09
Это отличный ответ, который укрепил мое понимание того, как все это работает. Благодарность! (В качестве примечания, это может быть более новая функция, но вы можете использовать ее 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