Pubsub Lists

05/01/2014, Thu
Categories: #JavaScript
Tags: #jQuery

Publish and Subscribe

One way of emitting events is by using trigger on a jQuery element.

// Event Emitting from Element
$("#my-element-id").trigger("my-beautiful-event");

This is okay if we only wanted to trigger an event from a specific element, but what happens if we want a generic way of doing things, such as without referring to an element when triggering?

The way to do this is to use jQuery.Callbacks() to create a pubsub system.

An instance of jQuery.Callbacks accumulates functions, and runs one functions after another when the "fire" method is call upon. The property of jQuery.Callbacks that is of interest in the pubsub system, is the ability to fire(call) the functions when needed, whereas the ability to accumulate multiple functions is not as critical to the pubsub system.

To demonstrate pubsub, our example will have three select lists where each subscribes and publishes to one another.

The code from the jQuery Api page will be used

// Pubsub
// Adapted from jQuery api page (only variable renames)
// https://api.jquery.com/jQuery.Callbacks/
var classGroups = {};
var pubsubNameSpace = function (id) {
  var callbacks,
    classGroup = id && classGroups[id];
  if (!classGroup) {
    callbacks = $.Callbacks();
    classGroup = {
      publish: callbacks.fire,
      subscribe: callbacks.add,
      unsubscribe: callbacks.remove,
    };
    if (id) {
      classGroups[id] = classGroup;
    }
  }
  return classGroup;
};

The main point of this code is that 'pubsubNameSpace' will create a namespace for a string that is passed in as 'id'. The 'id' is a key in the 'classGroups' object. The 'classGroup' is an object that is the return value of 'pubsubNameSpace' which has a 'publish', 'subscribe', and 'unsubscribe' key that refer to the jQuery Callbacks methods.

Once we have this code, the following can be called like so

// Pubsub Usage

// Create a namespace
var ns = pubsubNameSpace("my-creative-namespace-name");

// Subscribe to the Namespace
ns.subscribe(myFunction);

// Publish within the Namespace
ns.publish({
  data: "just-a-string",
});

// Subscribe's function
function myFunction() {
  console.log(arguments[0]);
}

When publishing, one can pass in any data, but with the subscribe call, a function must be use as a funnel for the data that has been published.

To start the example, the initial variable declarations:

// Select List New Namespace

var $select = $("select.classy"),
  className = $select.prop("class"),
  ns = pubsubNameSpace(className),
  $info = $("#info");

The class name of the select lists is used as the name space for the pubsub. The '$info' is the div for displaying messages.

The main logic for publishing and subscribing for the select lists.

// Select Lists Pubsub

var id = -1;
$select.each(function () {
  // To id each select list
  id++;
  var $this = $(this);
  $this.data("id", id);

  // Reset each list
  $this.prop("selectedIndex", 0);

  // Listen to the selection changes for each list
  $this.on("change", function () {
    var $selected = $this.children("option:selected");

    // Tell all members of the same class of change
    ns.publish({
      selected: $selected.text(),
      id: $this.data("id"),
    });
  });

  // Listen for changes within the same class
  ns.subscribe($.proxy(explain, $this));
});

Iterate through each select list and associate them with an id. Attach a 'change' event handler to each list and within it, publish the selected value with the select list id. Also subscribe to messages from the same class.

Now finally for the subscribe function

// Logging Function

// Log info for subscribe function
var explain = function () {
  if (this.data("id") !== arguments[0].id) {
    // Display the info in div
    var msg =
      "List " +
      this.data("id") +
      " heard from List " +
      arguments[0].id +
      ". The value selected was " +
      '"' +
      arguments[0].selected +
      '"';
    $info.prepend("<p>" + msg + "</p>");
  }
};

Note that there needs to be a check for whether the published data id is the same as the current list element's id to ensure that our list does not listen to its own published message. This is because all select lists listens subscribe to the same class namespace.

Online Demo | Offline Demo