Трансляция не получена в директиве

У меня есть отношения родитель-потомок контроллера между моими <main>, <div> а также <my-directive> в качестве таких:

<main ng-controller="MainController">

    <nav>
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
        </ul>
    </nav>

    <div ng-controller="AboutController">

    </div>

    <my-directive></my-directive>

</main>

В MainController я выполняю трансляцию $ с:

$scope.$broadcast('shoppingCartReady', msg);

В AboutController я могу успешно получать эту трансляцию $ через:

angular.module('aboutModule', [])
    .controller('AboutController', require('./about/aboutController'));

var AboutController = function ($scope, $rootScope) {

    $scope.$on('shoppingCartReady', function(event, msg){
        console.log(msg);
    });

};

module.exports = [ '$scope', '$rootScope', AboutController];

Тем не менее, при попытке получить трансляцию $ внутри <my-directive> Функция ссылки, кажется, никогда не будет получена:

angular.module('shopModule', [])
    .directive('shopModuleDirective', require('./shopModuleDirective'))
    .directive('myDirective', require('./myDirective'));

var myDirective = function ($rootScope) {

    function link($scope, element, attrs, baseCtrl) {

        $scope.$on('shoppingCartReady', function (event, msg) {
            console.log(msg);
        });
    }

    return {
        restrict: 'E',
        require: '^shopModuleDirective',
        link: link
    };
};

return ['$rootScope', myDirective];

ОБНОВИТЬ


Я даже создал контроллер в моей директиве:

angular.module('shopModule', [])
    .directive('shopModuleDirective', require('./shopModuleDirective'))
    .directive('myDirective', require('./myDirective'));

var myDirective = function ($rootScope) {

    var controller = ["$scope", "$element", function ($scope, element) {
        console.log('waiting for .on....');
        $scope.$on('shoppingCartReady', function (event, cache) {
             console.log('shoppingCartReady .on rcvd!');
        });
    }]

    function link($scope, element, attrs, baseCtrl) {

        $scope.$on('shoppingCartReady', function (event, msg) {
            console.log(msg);
        });
    }

    return {
        restrict: 'E',
        require: '^shopModuleDirective',
        link: link
    };
};

return ['$rootScope', myDirective];

Я вижу журнал для

console.log('waiting for .on....');

Но.on никогда не получен.

ОБНОВИТЬ:


Я думаю, что возможная причина в том, что сфера не "готова".

В моем оригинале broadcast из MainController, если я выполню другой после setTime из всех .on получены:

MainController:

$scope.$broadcast('shoppingCartReady', msg);

setTimeout(function(){
    $scope.$broadcast('shoppingCartReady', msg);
}, 8000);

2 ответа

Решение

Есть состояние гонки.

myDirective контроллер и функция post-link выполняются после MainControllerи слушатель настроен после shoppingCartReady событие было запущено.

Основное правило для хорошей, проверяемой конструкции директивы - сохранять всю связанную с областью логику в контроллере, связывая функции и $onInit hook используются именно для того, что должно происходить там, а не где-либо еще, например, для манипуляций с скомпилированным содержимым DOM.

Менее очевидный, но особый вариант использования link является желаемым порядком выполнения (функции постсвязывания выполняются в обратном порядке, от дочерних к родительским). Если MainController становится директивой, и scope.$broadcast('shoppingCartReady', msg) выполнен в link функция, а не контроллер, это гарантирует правильный порядок выполнения.

Оборачивание кода контроллера в ноль $timeout также отложит код

$timeout(function(){
    $scope.$broadcast('shoppingCartReady', msg);
});

будет выполняться после связывания функций в директивах child, но также будет обозначать некоторый запах кода. Это одна из причин, по которой события области видимости являются менее желательными способами директивной коммуникации и склонны быть анти-шаблонами.

Альтернативный способ справиться с этим зависит от того, что shoppingCartReady Это событие, но в текущем случае оно избыточно: оно уже произошло в тот момент, когда выполняются дочерние контроллеры, они могут смело предполагать, что "корзина покупок готова".

Предлагаемое чтение для этой темы: Директивный контроллер и синхронизация ссылок в AngularJS.

Из документации:

$ broadcast (имя, аргументы);

Отправляет имя события вниз всем дочерним областям (и их дочерним элементам), уведомляя зарегистрированных слушателей $ rootScope.Scope.

Так как в вашей директиве вы не создаете новую область, я думаю, вы находитесь на том же уровне области, что и ваш MainController, В этом случае вы не находитесь в childScope MainController поэтому событие не может быть инициировано там. Я бы попробовал одно из следующего:

  1. Трансляция ваших событий всегда через $rootScope.$broadcast, Все остальные области являются детьми $rootScope и поэтому везде, где вы регистрируетесь на мероприятие с $scope.$on, вы получите это. Вы также можете быть заинтересованы в том, почему мы используем трансляцию $rootScope.$ В AngularJS?

  2. Дайте вашу директиву изолированной $scope, Это будет действовать как childScope для MainController и должен работать так же. Выделение области действия директивы.

  3. В дополнение к опции 1. Вы также можете использовать $rootScope.$emit вместе с $rootScope.$on, Для получения дополнительной информации читайте $rootScope.$ Broadcast против $ scope. $ Emit

Я всегда выбирала вариант 1. как иногда вы не хотите, чтобы ваша директива имела изолированную область видимости.

Другие вопросы по тегам