Explain the communication mechanism of AngularJS in detail

  • 2020-06-15 07:50:02
  • OfStack

Now almost all over the world people are asking! Is there anyone out there? This is USS AngularJS, we have a problem, our service speaks Klingon and our controllers can't communicate with their Ferengi instructions. Can someone help us?

Already don't know how many times have I met about what AngularJS components inside the communications problems such as the best way. Many times the answer would be to use $rootScope object to anyone who wants to listen to radio $broadcast out 1 message. However, it is not the best way to do it. Broadcast messages between components means that they need to know how much 1 some other component coding details, thus limiting the modularization and reuse them.

In this article, I'll show you how to use the publish/subscribe pattern for internal component communication in AngularJS.


AngularJS are available in a variety of ways you used for communication among the components, and the most commonly used method is need you know too much about the details of how the components of communication, thus increasing the coupling between components, and reduce their degree of modularity and cohesion. It may make it harder for your components to reuse in other applications.

By using the publish/subscribe design pattern, we can reduce the coupling between components and encapsulate the details of their communication. This will help increase the modularity, testability, and reusability of your components.

The implementation of the publish/subscribe pattern I will describe is observed by Eric Burley, @eburley in its post angularjs. org on the publish/subscribe pattern.. Recommended in.

The sample application I have described will show you how you can apply the publish/subscribe pattern to internal controller communications and controller service communications. You can find the source code on GitHub under my repository, ES29en-ES30en.

First we need a communication channel

Let's start with the services that handle publish and subscribe information. I've defined a service interface that provides a way to publish and subscribe to information that we can use to process the information we want to exchange.

In the following code, I define two internals; _EDIT_DATA_, which means that we need to edit the data that follows, and _DATA_UPDATED_, which means that our data has been changed. These are defined internally and cannot be accessed by the user, which helps hide the implementation.

For each piece of information, there are two ways; One will publish the message to the subscriber, and the other will let the subscriber register a callback method, which will be called when the message is received.


The methods used to publish to subscribers are editData, on line 9, and dataUpated, on line 19. They push private notifications to pending events via the $rootScope.$broadcast method.

The method used to register the event, set up a listener with $scope.$on, and when a broadcast message is received, it takes turns to execute the events registered to the service by the subscriber. At the same time, since the subscriber needs its own scope to be passed in as a parameter, we can use it to perform the listening information, avoiding the complicated processing of maintaining the listener list. The method to register the event is onEditData at line 13 and onDataUpdated at line 23.

To hide the implementation details, I used Revealing Module (Reveal module: ugly name) mode, returning only those methods That I wanted the user to use.


angular.module(['application.services'])
  // define the request notification channel for the pub/sub service
  .factory('requestNotificationChannel', ['$rootScope', function ($rootScope) {
    // private notification messages
    var _EDIT_DATA_ = '_EDIT_DATA_';
    var _DATA_UPDATED_ = '_DATA_UPDATED_';
 
    // publish edit data notification
    var editData = function (item) {
      $rootScope.$broadcast(_EDIT_DATA_, {item: item});
    };
    //subscribe to edit data notification
    var onEditData = function($scope, handler) {
      $scope.$on(_EDIT_DATA_, function(event, args) {
        handler(args.item);
      });
    };
    // publish data changed notification
    var dataUpdated = function () {
      $rootScope.$broadcast(_DATA_UPDATED_);
    };
    // subscribe to data changed notification
    var onDataUpdated = function ($scope, handler) {
      $scope.$on(_DATA_UPDATED_, function (event) {
        handler();
      });
    };
    // return the publicly accessible methods
    return {
      editData: editData,
      onEditData: onEditData,
      dataUpdated: dataUpdated,
      onDataUpdated: onDataUpdated
    };
  }])

news

News is very simple, first of all we need in our controller for requestNotificationChannel introduced some rely on 1. You can see the dataService definition line 2 below. When the event occurs, to need to know if you need to have a change of other objects to send signals, you only need to call on requestNotificationChannel appropriate notification method is ok. If you noticed the saveHop dataService deleteHop and addHop method, you'll see they all call requestNotificationChannel dataUpdated method, This method will send a signal to the listener, which is already registered with the onDataUpdated method.


  // define the data service that manages the data
  .factory('dataService', ['requestNotificationChannel', function (requestNotificationChannel) {
    // private data
    var hops = [
      { "_id": { "$oid": "50ae677361d118e3646d7d6c"}, "Name": "Admiral", "Origin": "United Kingdom", "Alpha": 14.75, "Amount": 0.0, "Use": "Boil", "Time": 0.0, "Notes": "Bittering hops derived from Wye Challenger. Good high-alpha bittering hops. Use for: Ales Aroma: Primarily for bittering Substitutions: Target, Northdown, Challenger", "Type": "Bittering", "Form": "Pellet", "Beta": 5.6, "HSI": 15.0, "Humulene": 0.0, "Caryophyllene": 0.0, "Cohumulone": 0.0, "Myrcene": 0.0, "Substitutes": ""} ,
      { "_id": { "$oid": "50ae677361d118e3646d7d6d"}, "Name": "Ahtanum", "Origin": "U.S.", "Alpha": 6.0, "Amount": 0.0, "Use": "Boil", "Time": 0.0, "Notes": "Distinctive aromatic hops with moderate bittering power from Washington. Use for: Distinctive aroma Substitutes: N/A", "Type": "Aroma", "Form": "Pellet", "Beta": 5.25, "HSI": 30.0, "Humulene": 0.0, "Caryophyllene": 0.0, "Cohumulone": 0.0, "Myrcene": 0.0, "Substitutes": ""} ,
      { "_id": { "$oid": "50ae677361d118e3646d7d6e"}, "Name": "Amarillo Gold", "Origin": "U.S.", "Alpha": 8.5, "Amount": 0.0, "Use": "Boil", "Time": 0.0, "Notes": "Unknown origin, but character similar to Cascade. Use for: IPAs, Ales Aroma: Citrus, Flowery Substitutions: Cascade, Centennial", "Type": "Aroma", "Form": "Pellet", "Beta": 6.0, "HSI": 25.0, "Humulene": 0.0, "Caryophyllene": 0.0, "Cohumulone": 0.0, "Myrcene": 0.0, "Substitutes": ""} ,
      { "_id": { "$oid": "50ae677361d118e3646d7d6f"}, "Name": "Aquila", "Origin": "U.S.", "Alpha": 6.5, "Amount": 0.0, "Use": "Boil", "Time": 0.0, "Notes": "Aroma hops developed in 1988. Limited use due to high cohumolone.Used for: Aroma hops Substitutes: ClusterNo longer commercially grown.", "Type": "Aroma", "Form": "Pellet", "Beta": 3.0, "HSI": 35.0, "Humulene": 0.0, "Caryophyllene": 0.0, "Cohumulone": 0.0, "Myrcene": 0.0, "Substitutes": ""} ,
      { "_id": { "$oid": "50ae677361d118e3646d7d70"}, "Name": "Auscha (Saaz)", "Origin": "Czech Republic", "Alpha": 3.3, "Amount": 0.0, "Use": "Boil", "Time": 0.0, "Notes": " Use for: Pilsners and Bohemian style lagers Aroma: Delicate, mild, clean, somewhat floral -- Noble hops Substitute: Tettnanger, LublinExamples: Pulsner Urquell", "Type": "Aroma", "Form": "Pellet", "Beta": 3.5, "HSI": 42.0, "Humulene": 0.0, "Caryophyllene": 0.0, "Cohumulone": 0.0, "Myrcene": 0.0, "Substitutes": ""} ,
    ];
    // sends notification that data has been updated
    var saveHop = function(hop) {
      requestNotificationChannel.dataUpdated();
    };
    // removes the item from the array and sends a notification that data has been updated
    var deleteHop = function(hop) {
      for(var i = 0; i < hops.length; i++) {
        if(hops[i]._id.$oid === hop._id.$oid) {
          hops.splice(i, 1);
          requestNotificationChannel.dataUpdated();
          return;
        }
      };
    };
    // internal function to generate a random number guid generation
    var S4 = function() {
      return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
    };
    // generates a guid for adding items to array
    var guid = function () {
     return (S4() + S4() + "-" + S4() + "-4" + S4().substr(0,3) + "-" + S4() + "-" + S4() + S4() + S4()).toLowerCase();
    };
    // function to add a hop to the array and sends a notification that data has been updated
    var addHop = function(hop) {
      hops.id.$oid = guid();
      hops.push(hop);
      requestNotificationChannel.dataUpdated();
    };
    // returns the array of hops
    var getHops = function() {
      return hops;
    };
    // returns a specific hop with the given id
    var getHop = function(id) {
      for(var i = 0; i < hops.length; i++) {
        if(hops[i]._id.$oid === id) {
          return hops[i];
        }
      };
    };
    // return the publicly accessible methods
    return {
      getHops: getHops,
      getHop: getHop,
      saveHop: saveHop,
      deleteHop: deleteHop,
      addHop: addHop
    }
  }]);


Receive event notifications

From requestNotificationChannel receives the event notification is also very simple, additional callback handler we only needed to use the notification when messages are sent to do 1 some of their processing. We will need to add some dependence to 1 again for our controller requestNotificationChannel, services, and instructions, you can see these in the following code line 2. Next, we need to define an event callback handler to respond to the event notification, You can see this in line 5 below. Then we need to register our callback handler to requestNotificationChannel by calling the onDataUpdated method and passing in the scope from the controller and callback handler, which we do in line 9.



  //define the controller for view1
  .controller('view1-controller', ['$scope', 'dataService', 'requestNotificationChannel', function($scope, dataService, requestNotificationChannel) {
    $scope.hops = dataService.getHops();
 
    var onDataUpdatedHandler = function() {
      $scope.hops = dataService.getHops();
    }
 
    requestNotificationChannel.onDataUpdated($scope, onDataUpdatedHandler);
 
    $scope.onEdit = function(hop) {
      requestNotificationChannel.editData(hop);
    }
 
    $scope.onDelete = function(hop) {
      dataService.deleteHop(hop);
    }
  }]);

A controller used for controller communication

We can also use the requestNotificationChannel for communication between controllers. We only need one controller to play the role of publisher and one controller to play the role of subscriber. If you look at the onEdit method of ES99en1-ES100en on line 11 of the previous code, you will see that it sends an editData message. The following view2-controller is registered with requestNotificationChannel for its onEditDataHandler from lines 5 to 9, so whenever view1-controller1 sends an editData message with the item to be modified, ES111en2-ES112en is notified by the editData message, gets the item and updates it to its model.


  //define the controller for view1
  .controller('view2-controller', ['$scope', 'dataService', 'requestNotificationChannel', function($scope, dataService, requestNotificationChannel) {
    $scope.hop = null;
 
    var onEditDataHandler = function(item) {
      $scope.hop = item;
    };
 
    requestNotificationChannel.onEditData($scope, onEditDataHandler);
 
    $scope.onSave = function() {
      dataService.saveHop($scope.hop);
      $scope.hop = null;
    }
 
    $scope.onCancel = function() {
      $scope.hop = null;
    }
  }]);

Write a good interface document

One thing that might be overlooked is that we use communication interfaces between components, and these interfaces need a good document on how they should be used. In the above example, if there is no documentation, the user will not know that onEditData is sending a callback function with data to be edited. So when you start using this pattern, the good trick is to annotate the method to make sure that the notification service knows exactly what's going on.
conclusion

Ok, so we've discussed how to use the subscribe/publish pattern in your AngularJS application for intermodule communication. This pattern allows you to decouple your modules from internal messages and reuse them more easily. You can even replace all communication between modules with subscription/publish mode. This pattern works well especially if your service has a lot of asynchronous requests and you want to cache the data in the service to reduce communication with the server.

I hope this is helpful, you can find the code for the example under my GitHub warehouse, angularjs-ES130en.


Related articles: