How to implement emit to work in a synchronous manner

420 views Asked by At

I am trying to display a spinner during page load using angular $emit and a command file. My model is:

Model:

    model.load = function(){
        model.loading = true;
        $rootScope.$emit('loadMyPage', model.loading);
        return service.getData().then(storesResult, storesFault);
    }


       var storesResult = function (value) {
        model.categoriesLoading = false;
        $rootScope.$emit('loadMyPage', model.loading);
        model.stores = value.result;
        saveData();
    };

    var storesFault = function (value) {
        var data = value.data;
        var status = value.status;
        model.categoriesLoading = false;
        model.stores = null;
        $rootScope.$emit('loadMyPage', model.loading);
        $rootScope.$emit('systemAlert', {title: 'Loading Error', message: data, status: status, type: 'danger', timeout: 10000, showAsModal: true});
        return value;
    };

My Command file which is executed in app.run

command.execute = function () {
            $rootScope.$on('loadMyPage', onLoadingChange);
        };

        var onLoadingChange = function (event, value) {
            if (model.loading === true) {
                showModal('sections/modals/loading/loading.tpl.html');
            } else {
                hideModal();
            }
        };
};

The problem is during page load the $emit from model.load does not go to $on in command. When the $on is called it is done from the storesResult block. As a result model.loading is always getting false. This can be an async issue.

All suggestions are welcome.

1

There are 1 answers

0
SoEzPz On

Maybe don't use $emit and $on. If you have the $rootscope object, just make a key with your key value pair or whatever data you want to check for. Your data may be there as you would expect, or use $watch to wait for it, or just use a callback with $rootscope.

Model:

    model.load = function(){
      // model.loading = true;
      $rootScope.isLoading = true;
      return service.getData().then(storesResult, storesFault);
    }


    var storesResult = function (value) {
      model.categoriesLoading = false;
      $rootScope.isLoading = true;
      model.stores = value.result;
      saveData();
    };

    var storesFault = function (value) {
      var data = value.data;
      var status = value.status;
      model.categoriesLoading = false;
      model.stores = null;
      $rootScope.isLoading = true;
      $rootScope.$emit('systemAlert', {title: 'Loading Error', message: data, status: status, type: 'danger', timeout: 10000, showAsModal: true});
    return value;
};

command.execute = function () {
        $rootScope.$watch('isLoading', onLoadingChange);
    };

    var onLoadingChange = function ( value ){
        if (value === true) {
        // or simply if( value )
            showModal('sections/modals/loading/loading.tpl.html');
        } else {
            hideModal();
        }
    };
};

HOWEVER

Maybe $rootScope is not the best idea to pollute with attributes. I'll leave that one up to you.

ALSO

A quick help out on $emit and $broadcast

Controllers are instantiated, with $scope DI'd => link ( not singletons );

This is how broadcast and emit work. Notice the nodes below; all nested within node 3. You use broadcast and emit when you have this scenario.

                   3
               --------
              |        |
             ---     ----
            1   |    2   |
          ---  ---  --- ---
          |  | |  | | | | |

Check out this tree. How do you answer the following questions? note: There are other ways, but here we'll discuss broadcast and emit. Also, when reading below text assume each number has it's own file (directive, controller) e.x. one.js, two.js, three.js.

  1. How does 1 speak to 3?

In file one.js

scope.$emit('messageOne', someValue(s));

In file three.js

scope.$on('messageOne', someValue(s));
  1. How does 2 speak to 3?

In file two.js

scope.$emit('messageTwo', someValue(s));

In file three.js

scope.$on('messageTwo', someValue(s));
  1. How does 3 speak to 1 and/or 2?

In file three.js

scope.$broadcast('messageThree', someValue(s));

In file one.js && two.js whichever file you want to catch the message.

scope.$on('messageThree', someValue(s));
  1. How does 2 speak to 1?

In file two.js

scope.$emit('messageTwo', someValue(s));

In file three.js

scope.$on('messageTwo', function( event, data ){
  scope.$broadcast( 'messageTwo', data );
});

In file one.js

scope.$on('messageTwo', someValue(s));

HOWEVER

When you have all these nested child nodes trying to communicate like this, you will quickly see many $on's $broadcast's, and $emit's. Here is what I like to do.

In PARENT NODE ( three in this case... )

So, in file three.js

scope.$on('pushChangesToAllNodes', function( event, message ){
  scope.$broadcast( message.name, message.data );
});

Now in any of the child nodes you only need to $emit the message or catch it using $on.

NOTE: It is normally quite easy to cross talk in one nested path without using $emit, $broadcast, or $on, which means most use cases are for when you are trying to get 1 to communicate with 2 or vice versa.

  1. How does 2 speak to 1?

In file two.js

scope.$emit('pushChangesToAllNodes', sendNewChanges());

function sendNewChanges(){
  return { name: 'talkToOne', data: [1,2,3] };
}

In file three.js

We already handled this one remember?

In file one.js

scope.$on('talkToOne', function( event, arrayOfNumbers ){
  arrayOfNumbers.forEach(function(number){
    console.log(number);
  });
});

You will still need to use $on with each specific value you want to catch, but now you can create whatever you like in any of the nodes without having to worry about how to get the message across the parent gap.

Hope this helps...