How can I call a function after receiving two or more scope events?

For example, suppose I need to run a function after receiving two events, "eventA" and "eventB". I usually declare a boolean variable for each event, setting the variable to true when the event is received and asking if another variable is true to trigger the function:

var a = false, b = false; $scope.$on("eventA", function(){ a = true; if (b) performTask(); }); $scope.$on("eventB", function(){ b = true; if (a) performTask(); }); var performTask = function() { /* do something... */ }; 

It becomes more difficult if there are three or more events. Is there a design pattern to handle these cases?

+5
source share
4 answers

You can use $ q promises.

 var dfdATask= $q.defer(); var dfdBTask= $q.defer(); $scope.$on("eventA", function(){ // whatever this function does dfdATask.resolve(true);//or pass a value }); $scope.$on("eventB", function(){ //whatever this function does dfdBTask.resolve(true);//or pass a value }); $q.all([dfdATask.promise, dfdBTask.promise]).then(function(){ //be sure to pass in an array of promises //perform task }) 
+3
source

active polling using $ scope. $ watch:

One way to do this:

 var a = false, b = false; $scope.$on("eventA", function(){ a = true; }); $scope.$on("eventB", function(){ b = true; }); $scope.$watch( function() { return a && b; }, function(newval, oldval) { if (newval) { performTask(); } } ); 

one more step:

 var events = { a: false, b: false }; $scope.$on("eventA", function(){ events.a = true; }); $scope.$on("eventB", function(){ events.b = true; }); $scope.$watch( function() { var result = true; for (var key in events) { result = result && events[key]; } return result; }, function(newval, oldval) { if (newval) { performTask(); } } ); 

http://plnkr.co/edit/5NrOhTwblMCCCoKncVAW?p=preview

Be sure to read the developer’s guide and view the “Performance Watch” section .. p>

regular callback:

 var events = { a: false, b: false }; function checkIfPerfomTask() { for (var key in events) { if (!events[key]) { return; } } performTask(); } $scope.$on("eventA", function(){ events.a = true; checkIfPerfomTask(); }); $scope.$on("eventB", function(){ events.b = true; checkIfPerfomTask(); }); 

http://plnkr.co/edit/5NrOhTwblMCCCoKncVAW?p=preview

with one promise, $ q.defer ():

 var events = { a: false, b: false }; var shouldPerform = $q.defer(); function checkIfPerfomTask() { for (var key in events) { if (!events[key]) { return; } } shouldPerform.resolve(); } $scope.$on("eventA", function(){ events.a = true; checkIfPerfomTask(); }); $scope.$on("eventB", function(){ events.b = true; checkIfPerfomTask(); }); shouldPerform.promise.then(performTask); 

http://plnkr.co/edit/5NrOhTwblMCCCoKncVAW?p=preview

with several promises ...

Several answers have already been considered.

+1
source

So, the theory is reasonable, if you only want to perform this magical action after you received these two events, was called at least once, then you probably want to use promises.

 app.controller('ExampleOneController', [ '$log', '$scope', '$q', '$rootScope', function ($log, $scope, $q, $rootScope) { $scope.anotherAction1FiredCount = 0; var aDeferred = $q.defer(), bDeferred = $q.defer(); $scope.$on('e-1-a', function () { $log.log('Fired e-1-a'); aDeferred.resolve(); }); $scope.$on('e-1-b', function () { $log.log('Fired e-1-b'); bDeferred.resolve(); }); $q.all([aDeferred.promise, bDeferred.promise]).then(function () { $log.log('Fired another action 1!'); $scope.anotherAction1 = 'Hello World 1!'; $scope.anotherAction1FiredCount++; }); } ]); 

As usual, I want to perform every time two things happen, so I tend to reset 'my promises.

 app.controller('ExampleTwoController', [ '$log', '$scope', '$q', function ($log, $scope, $q) { $scope.anotherAction2FiredCount = 0; var aDeferred = $q.defer(), bDeferred = $q.defer(); $scope.$on('e-2-a', function () { $log.log('Fired e-2-a'); aDeferred.resolve(); }); $scope.$on('e-2-b', function () { $log.log('Fired e-2-b'); bDeferred.resolve(); }); var wait = function () { $q.all([aDeferred.promise, bDeferred.promise]).then(function () { $log.log('Fired another action 2!'); $scope.anotherAction2 = 'Hello World 2!'; $scope.anotherAction2FiredCount++; aDeferred = $q.defer(); bDeferred = $q.defer(); wait(); }); }; wait(); } ]); 

Here is the working pluker!

Promises is life.

+1
source

Promises are for use. But since you mentioned that you were looking for a design pattern, I will put one way to do this using the Observer pattern.

You can check this live Plunkr: http://plnkr.co/edit/1Oqn2TAGTr7NLYZd9ax1?p=preview

It has an angularjs function that processes event tracking logic and invokes the final action.

The controller simply detects your events, the final event and logs them with your service.

 app.controller('MainCtrl', function($scope, EventService) { var events = []; ... //define events EventService.registerEvents(events); EventService.registerEventsCallback(finalEvent); //the observer }); 

The service does this job by removing the called event from the list of events on first execution.

 app.factory('EventService', function(){ var events = []; var finalEvent; var eventsCallback = function(){ if(!events.length){ finalEvent(); } } var resolveEvent= function(event){ var eventIndex = events.indexOf(event); if(eventIndex>=0){ events.splice(eventIndex,1); } } return{ registerEvents: function(eventsList){ events = angular.copy(eventsList); }, registerEventsCallback: function(event){ finalEvent = event; }, publishEvent: function(event){ event(); resolveEvent(event); eventsCallback(); } } }); 
0
source

Source: https://habr.com/ru/post/1209409/


All Articles