$ on и $ трансляция в угловых

282

У меня есть footerController и codeScannerController с различными представлениями.

angular.module('myApp').controller('footerController', ["$scope", function($scope) {}]);

angular.module('myApp').controller('codeScannerController', ["$scope", function($scope) {
console.log("start");
$scope.startScanner = function(){...

Когда я нажимаю на <li>файл footer.html, я должен получить это событие в codeScannerController.

<li class="button" ng-click="startScanner()">3</li>

Я думаю, что это можно реализовать с помощью $onи $broadcast, но я не знаю, как и не могу найти примеры нигде.

Алиса Полански
источник

Ответы:

631

Если вы хотите $broadcastиспользовать $rootScope:

$scope.startScanner = function() {

    $rootScope.$broadcast('scanner-started');
}

А затем, чтобы получить, используйте $scopeсвой контроллер:

$scope.$on('scanner-started', function(event, args) {

    // do what you want to do
});

Если вы хотите, вы можете передать аргументы, когда вы $broadcast:

$rootScope.$broadcast('scanner-started', { any: {} });

А затем получите их:

$scope.$on('scanner-started', function(event, args) {

    var anyThing = args.any;
    // do what you want to do
});

Документация для этого в Scope docs .

Дэвин Трайон
источник
2
Вы можете назвать событие как угодно.
Дэвин Трайон
5
Убедитесь, что вы $ scope. $ Apply (); ваши изменения!
Исмаил
4
@ Почта Почему ... и где?
Jaans
7
Есть ли рекомендуемые методы для хранения этих строк где-нибудь, а не жесткое кодирование широковещательного сообщения?
rperryng
8
@Ismail $scope.$apply()необходим только при изменении модели вне угловой структуры (как в setTimeout, обратном вызове диалога или обратном вызове ajax), другими словами, $apply()он уже запущен после завершения всего кода в .$on().
th3uiguy
97

Во- первых, краткое описание $on(), $broadcast()и$emit() :

  • .$on(name, listener) - прослушивает конкретное событие по заданному name
  • .$broadcast(name, args)- Трансляция события через $scopeвсех детей
  • .$emit(name, args)- Создайте событие в $scopeиерархии для всех родителей, включая$rootScope

На основе следующего HTML (см. Полный пример здесь ):

<div ng-controller="Controller1">
    <button ng-click="broadcast()">Broadcast 1</button>
    <button ng-click="emit()">Emit 1</button>
</div>

<div ng-controller="Controller2">
    <button ng-click="broadcast()">Broadcast 2</button>
    <button ng-click="emit()">Emit 2</button>
    <div ng-controller="Controller3">
        <button ng-click="broadcast()">Broadcast 3</button>
        <button ng-click="emit()">Emit 3</button>
        <br>
        <button ng-click="broadcastRoot()">Broadcast Root</button>
        <button ng-click="emitRoot()">Emit Root</button>
    </div>
</div>

Запущенные события будут проходить $scopesследующим образом:

  • Трансляция 1 - будет видна только контроллеру 1 $scope
  • Испустите 1 - Будет видно на контроллере 1 , $scopeто$rootScope
  • Трансляция 2 - будет виден контроллером 2, $scopeзатем контроллером 3$scope
  • Испустите 2 - Будет видно на контроллере 2 , $scopeто$rootScope
  • Трансляция 3 - будет видна только контроллеру 3 $scope
  • Emit 3 - будет виден контроллером 3 $scope, $scopeзатем контроллером 2$rootScope
  • Broadcast Root - будет рассматриваться $rootScopeи $scopeвсе контроллеры (1, 2 , то 3)
  • Emit Root - будет виден только $rootScope

JavaScript для запуска событий (снова, вы можете увидеть рабочий пример здесь ):

app.controller('Controller1', ['$scope', '$rootScope', function($scope, $rootScope){
    $scope.broadcastAndEmit = function(){
        // This will be seen by Controller 1 $scope and all children $scopes 
        $scope.$broadcast('eventX', {data: '$scope.broadcast'});

        // Because this event is fired as an emit (goes up) on the $rootScope,
        // only the $rootScope will see it
        $rootScope.$emit('eventX', {data: '$rootScope.emit'});
    };
    $scope.emit = function(){
        // Controller 1 $scope, and all parent $scopes (including $rootScope) 
        // will see this event
        $scope.$emit('eventX', {data: '$scope.emit'});
    };

    $scope.$on('eventX', function(ev, args){
        console.log('eventX found on Controller1 $scope');
    });
    $rootScope.$on('eventX', function(ev, args){
        console.log('eventX found on $rootScope');
    });
}]);
th3uiguy
источник
Как я могу представить иерархию моего приложения на примере, который вы привели. Как контроллер может быть родителем или ребенком? Я пытаюсь сказать, что у меня есть ряд состояний, например. LoginCtrl -> homeCrl -> messagesCtrl и так далее.
ХИРА ТАКУР
26

Вы должны знать, что префикс $ относится к угловому методу, префикс $$ относится к угловым методам, которые следует избегать.

ниже приведен пример шаблона и его контроллеров, мы рассмотрим, как $ broadcast / $ on может помочь нам достичь того, чего мы хотим.

<div ng-controller="FirstCtrl">
    <input ng-model="name"/> 
    <button ng-click="register()">Register </button>
</div>

<div ng-controller="SecondCtrl">
    Registered Name: <input ng-model="name"/> 
</div>

Контроллеры

app.controller('FirstCtrl', function($scope){
    $scope.register = function(){

    }
});

app.controller('SecondCtrl', function($scope){

});

Мой вопрос к вам, как вы передаете имя второму контроллеру, когда пользователь нажимает на регистр? Вы можете предложить несколько решений, но одно из них, которое мы собираемся использовать, - это использование $ broadcast и $ on.

$ широковещание против $ emit

Что мы должны использовать? $ broadcast будет направлен вниз ко всем дочерним элементам dom, а $ emit направит противоположное направление ко всем элементам dom предков.

Лучший способ избежать выбора между $ emit или $ broadcast - это канал из $ rootScope и использование $ broadcast для всех его дочерних элементов. Что делает наш случай намного проще, так как наши доминантные элементы - братья и сестры.

Добавление $ rootScope и позволяет трансляцию

app.controller('FirstCtrl', function($rootScope, $scope){
    $scope.register = function(){
        $rootScope.$broadcast('BOOM!', $scope.name)
    }
});

Обратите внимание, что мы добавили $ rootScope и теперь используем $ broadcast (broadcastName, arguments). Для broadcastName мы хотим дать ему уникальное имя, чтобы мы могли его перехватить в нашем secondCtrl. Я выбрал БУМ! просто для удовольствия. Второй аргумент 'arguments' позволяет нам передавать значения слушателям.

Получение нашей трансляции

Во втором контроллере нам нужно настроить код для прослушивания нашей трансляции.

app.controller('SecondCtrl', function($scope){
  $scope.$on('BOOM!', function(events, args){
    console.log(args);
    $scope.name = args; //now we've registered!
  })
});

Это действительно так просто. Живой пример

Другие способы достижения аналогичных результатов

Старайтесь избегать использования этого набора методов, поскольку он не эффективен и не прост в обслуживании, но это простой способ устранения проблем, которые могут возникнуть.

Обычно вы можете делать то же самое, используя сервис или упрощая свои контроллеры. Мы не будем обсуждать это подробно, но я решил упомянуть об этом для полноты.

И, наконец, имейте в виду, что действительно полезная трансляция для прослушивания - это '$ destroy', снова вы можете видеть, что $ означает, что это метод или объект, созданный кодами поставщиков. В любом случае $ destroy транслируется, когда контроллер уничтожается, вы можете послушать это, чтобы узнать, когда ваш контроллер удален.

Ян Ли
источник
2
В качестве предупреждения старайтесь не использовать слишком много трансляций / излучений в своем приложении. Управлять ими очень сложно, особенно в больших приложениях, так как отследить корни этих событий очень сложно.
Ян Ли
1
//Your broadcast in service

(function () { 
    angular.module('appModule').factory('AppService', function ($rootScope, $timeout) {

    function refreshData() {  
        $timeout(function() {         
            $rootScope.$broadcast('refreshData');
        }, 0, true);      
    }

    return {           
        RefreshData: refreshData
    };
}); }());

//Controller Implementation
 (function () {
    angular.module('appModule').controller('AppController', function ($rootScope, $scope, $timeout, AppService) {            

       //Removes Listeners before adding them 
       //This line will solve the problem for multiple broadcast call                             
       $scope.$$listeners['refreshData'] = [];

       $scope.$on('refreshData', function() {                                                    
          $scope.showData();             
       });

       $scope.onSaveDataComplete = function() { 
         AppService.RefreshData();
       };
    }); }());
Сэнди
источник