December 06, 2016 - VitoMd and the Evil Machine

Tag communication in Riot.js - part 2

Introduction

This is a follow up from our past article about tag communication in Riot

Any to any communication

In this tutorial I will explain the last form of communication, and implement some patterns to make the solution simple and scalable.

Let´s imagine that our application has several components that can interact between them. We should aspire to have independent components that will work in any application, do one thing and do it well. In general is better to have dumb components, this means that they have little logic and just display the information received. If the tags are parent-child the communication is easy , we just use the opts variable provided by Riot, but if the context is different we should use another pattern.

The Observable pattern

Riot.js provides a really useful tool called Observable, is based on the Observable pattern and is used to receive and send events (or messages). It´s goal is to isolate components without coupling them. Riot tags automatically are observables, but you can add the Observable support to any javascript object.

  function Car() {
    // Make Car instances observable
    riot.observable(this)
    // listen to 'start' event
    this.on('start', function() {
      // engine started
    })
  }

  // make a new Car instance
  var car = new Car()

  // trigger 'start' event
  car.trigger('start')

This is an example from the Riot.js documentation http://riotjs.com/es/api/observable/ , it´s make the instances of Car observable so the object is able to trigger and listen to events.

This is a list of some useful methods that the Observable provides:

  • el.on(events, callback): Listen to the given space separated list of events and execute the callback each time an event is triggered
  • el.one(event, callback): Listen to the given space separated list of events and execute the callback at most once
  • el.off(events): Removes the given space separated list of events listeners.
  • el.trigger(event, arg1 … argN): Execute all callback functions that listen to the given event. Any number of extra parameters can be provided for the listeners.

The two most useful methods are on that will listen to events and trigger that will send a message. In this example we trigger the hello-event with a parameter Hello! and it will be listened by the on method, the parameter will be received and the function executed.

  // listen to hello-event and expect arguments
  el.on(hello-event, function(greeting) {
    self.hi = greeting
  })

  // trigger start event with one parameter
  el.trigger('hello-event', 'Hello!')

Global communication

To make a global communication system in an application, we are going to create a global Store where all tags can send and receive messages. We are going to communicate two tags called Leia and Luke that in this case don´t belong to a father. This kind of communication is useful when we have few components and can manage the app logic in the store.

First we have the global store, where we add the observable support, so we can trigger and listen for events.

  //Store.js
  var Store = function(){
    riot.observable(this)
  }

In the index.html we will import the Store and attach it to the riot variable so it´s global and accessible to all tags, like this

  riot.store = new Store()

So the index.html will look like

  <leia></leia>
  <luke></luke>

  <script type="text/javascript" src="https://rawgit.com/riot/riot/master/riot%2Bcompiler.min.js"></script>
  <script type="riot/tag" src="./luke.tag"></script>
  <script type="riot/tag" src="./leia.tag"></script>
  <script src="./Store.js"></script>

  <script type="text/javascript">
    riot.store = new Store()
    riot.mount('* ')
  </script>

In the Leia tag we will have a button that when pressed will trigger and send the message to the store

  <leia>
    <button onclick={say_hi}>Say Hi to Luke</button>
    <script>
      say_hi() {
        riot.store.trigger('hello', 'Hello, from Leia')    
      }
    </script>  
  </leia>

Meanwhile in Luke tag we will listen for the hello event and when we receive that message, will update the hi variable and show it in the screen.

  <luke>
    <span>{this.hi}</span>
    <script>
      self = this
      self.hi = 'Luke'
      riot.store.on('hello', function(greeting) {
        self.hi = greeting
        self.update()
      })
    </script>
  </luke>

This method can be used to communicate any tag in your application easily and decoupled, because when you send a message and there are no receivers it will behave well without errors, making the components more independent and the application modular and robust.

Here is the working example

Other options

Riot.js doesn’t force you to use a specific pattern for component communication. For example you can use Flux or Redux patterns if you want.

There are some popular implementation like RiotControl (flux-like event controller) that uses the Observable pattern and can manage multiple stores, that it can work very well with many components as we organize the application logic in several stores. Here is a simple example:

  RiotControl.addStore(new TodoStore()) // Create and register the store in central dispatch.
  RiotControl.trigger('todo_add', { title: self.text }) // Send message from any tag
  RiotControl.on('todos_changed', function(items) { // Receive the message in the Store
      self.items = items
      self.update()
  })

Access this link for more info about RiotControl

Share this post on Twitter

Comments