Close

Learn ES6 Today with Vaadin Elements - Part 1: Promises

The Open Web Platform is moving faster than ever, with an ever evolving set of tools, features, and technologies. It is sometimes difficult to keep up with updated APIs, new constructs, and changes to existing workflows.

In this blog series, I’ll show you some new features of the web platform that you can start working with today. Over the course of several posts, you’ll learn better and easier ways to accomplish the same task.

We will be using an example using <vaadin-grid> to show the difference between the old method and the new features of ES6. In this example, we will cover Promises as a way to write asynchronous code in a top-down way.

I've divided this guide into the "old" way of using callbacks for asynchronous functions, and the ES6 way of using Promises. Skip ahead to learn just about Promises.

 

What is ES6?

First let’s quickly go over some basic terms and concepts. The latest version of Javascript is called ECMAScript2015, or ES2015, and more commonly known as ES6. ES6 brings a host of new updates to the language, some of which we’ll cover in this blog series.

ECMAScript (ES) is the standardized language that Javascript implements. The two are nearly synonymous today and the exact distinction is unimportant.
See this post by Ben McCormick for a brief explanation.

The “old” way

In this short example, we will populate a <vaadin-grid> with a set of random users with their nationalities written out in full.

You can view and play with the sample on jsbin here.

Note: I will skip the basics of imports, styles and <vaadin-grid> HTML setup. See our Vaadin Elements guide for an introduction to <vaadin-grid>.

[.. imports, styles, and HTML elided ..]

<script>
  var grid = document.getElementById('grid');

  // grid.items is called implicitly to fill in the grid
  grid.items = function(params, callback) {


    // First grab a list of users
    var url = 'https://randomuser.me/api?index=' + params.index + '&results=' + params.count;
    getJSON(url, function(users) {
      
      // Then replace the short nationality code with the full country name
      users.results.forEach(function(user) {
        var url2 = 'https://restcountries.eu/rest/v1/alpha/' + user.nat;
        getJSON(url2, function(natData) {
          user.nat = natData.name;
          accumulate(user);
        })
      });
      
      // Gather final asynchronous results
      var waiting = users.results.length;
      var results = [];
      var accumulate = function(entry) {
        results.push(entry);
        waiting--;
        
        if (waiting == 0) {          
          // Call grid's callback to populate grid
          callback(results);
        }
      };
    });
  };

  // Send a request to url and call callback with the results
  function getJSON(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        callback(JSON.parse(xhr.responseText));
      }
    };
    xhr.open('GET', url, true);
    xhr.send();
  }
</script>

Getting the data

This guide is all about populating the grid with items, so our work will be in the grid.items function. grid.items is a function that is called implicitly by the grid element when it loads data from the server.

First we will request a list of users from http://randomusers.me with first name, last name, and a three letter nationality abbreviation.
We will then send requests to https://restcountries.eu/ to get the full nationality text for each user.
Lastly, we will compile the full list of users with long form nationalities and call the grid items callback with that list, which will populate our grid.

Here it is step by step:

First make a request for the users and pass in a callback function to the request.

// First grab a list of users
var url = 'https://randomuser.me/api?index=' + params.index + '&results=' + params.count;
getJSON(url, function(users) { 
  ... 
});

Then for each user in the retrieved data, we will run another request to get the full nationality name.

getJSON(url, function(users) {   
  users.results.forEach(function(user) {
    var url2 = 'https://restcountries.eu/rest/v1/alpha/' + user.nat;
    getJSON(url2, function(natData) {
      user.nat = natData.name;
      accumulate(user);
    });
   });

This calls the accumulate(entry) function, which gathers a new copy of the user, now with the full nationality.

Because our HTTP request are asynchronous, we have to wait until all of them are complete before we can finally run the grid items callback and populate the grid.

// Gather final asychronous results
var waiting = data.results.length;
var finalList = [];
var accumulate = function(user) {
  finalList.push(entry);
  waiting--;

  if (waiting == 0) {          
    // Call grid's callback to populate grid
    callback(finalList);
  }
};

If you are confused by this example, don’t feel bad, I was, too. The problem with this kind of asynchronous code is the necessity of callbacks to signal when an async request has completed. This is sometimes known as “callback hell” and is what happens when you try to write top-down code where one function requires waiting for another function to complete. It’s messy, but there are ways to make more readable code.

The promise of Promises

What if you could write similar straightforward code without having to pass around a myriad callbacks? ES6 finally brings Promises to Javascript, which make writing asynchronous code much simpler. Promises have been around for awhile in other languages and libraries, but ES6 brings first class Promises directly to the language.

A Promise represent a value that will be available some time in the future. This means the Promises are a stand-in for the result of some asynchronous behaviour. You work with Promises as though data is already available and write code top down that consumes that data.

Quick introduction to Promises

You create a Promise by calling its constructor and passing in a function with two arguments, which themselves are functions, so:

var promise = new Promise(function(resolve, reject) { ... });

This sets up a Promise which resolves to a value when resolve is called. For example:

var x = new Promise(function(resolve, reject) {
  // do some work
  resolve(10);
});

Which will set the value of x to a Promise that resolves to 10 once it has finished its work. But we can’t actually use that value of 10 until the Promise is consumed. To consume this value, we need to use Promise.then().

Promise.then() takes a function for input with an argument representing the fulfilled value of the Promise. Inside the then function, we can actually use the resolved value of the Promise.

For example:

var y = new Promise(function(resolve, reject) {
  // do some work
  resolve(20);
}).then(function(value) {
  var result = value + 5;
  console.log(result);
});

This will create a Promise which will eventually resolve to 20 that will be consumed by the then function and print 25 to the console.

You can also chain then statements to do extended work with Promises. If you return a value inside a then function, then you will return a new Promise that will resolve to the returned value.

var z = new Promise(function(resolve, reject) {
  // do some work
  resolve(5);
}).then(function(value) {
  var result = value + 20;
  return result;
}).then(function(value) {
  var finalresults = value + 30;
  console.log(finalresults);
});

This will create a Promise that resolves to 5, which then is chained with that value to another Promise, which will eventually resolve to 25. Then we use that new value to log 55 to the console.

Now let’s return to our “callback hell” example and make it work in a more straightforward manner.

Note that I gloss over the reject aspect of Promises. In addition to resolving a Promise, you can reject if the Promise throws an error or for whatever reason cannot resolve to a value. We also ignored error checking in our first example for simplicity’s sake.

You can read more about Promises and error checking at MDN here.

The ES6 way

We’re going to rewrite the logic in the example using Promises.
You can follow along and try out the new code at jsbin here.

[ ... HTML and imports elided ... ]


<script>
  var grid = document.getElementById('grid');

  grid.items = function(params, callback) {
    var url = 'https://randomuser.me/api?index=' + params.index + '&results=' + params.count;
    
    getJSONPromise(url).then(function(users) {
      var promises = users.results.map(function(user) {
        var url2 = 'https://restcountries.eu/rest/v1/alpha/' + user.nat;
        
        return getJSONPromise(url2).then(function(natData) {
          user.nat = natData.name;          
          return entry;
        });
      });
      
      return Promise.all(promises);
    }).then(function(results) {
      callback(results);
    });
  };

  function getJSONPromise(url) {
    return new Promise(function(resolve, reject) {
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
          resolve(JSON.parse(xhr.responseText));
        }
      };
      xhr.open('GET', url, true);
      xhr.send();
    });
  }
</script>

Setup

First, we rewrite the getJSON function to return a Promise that will resolve when the HTTP request return and call it getJSONPromise.

function getJSONPromise(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      }
    };
    xhr.open('GET', url, true);
    xhr.send();
  });
}

This is exactly the same as before, but here we return a new Promise that resolves to the results when the request is complete. We can now rewrite our callback chain into a chain of Promises.

Promises, Promises

The first part of the code looks exactly the same, except now we call getJSONPromise and instead of passing in a callback to be called when the request is done, we instead just chain a then to the Promise.

var grid = document.getElementById('grid');

grid.items = function(params, callback) {
  var url = 'https://randomuser.me/api?index=' + params.index + '&results=' + params.count;
    
  getJSONPromise(url).then(...);
};

Next let’s look at the body of the then function, which requires a little bit of understanding.

getJSONPromise(url).then(function(users) {
  var promises = users.results.map(function(user) { ... return a Promise ... });
  ... 
}); 

What we are doing here is first creating an array of Promises by mapping a function onto the array of users. See this MDN entry to learn more about Array.map()

Let’s take a look at the function being mapped onto the list of users.

var promises = users.results.map(function(user) {
  var url2 = 'https://restcountries.eu/rest/v1/alpha/' + user.nat;
      
  return getJSONPromise(url2).then(function(natData) {
    user.nat = natData.name;          
    return entry;
  });
});

Here we send a new HTTP request for each user and when that request completes, we set its 3 letter nationality to the full nationality. We then return entry which implicitly creates a new Promise that resolves to the value of the update user. The new Promise for each user is then added to an array of Promises called promises.

And then finally we bring it all together.

getJSONPromise(url).then(function(users) {
  var promises = ...

  return Promise.all(promises);
}).then(function(results) {
  callback(results);
});

We then return Promise.all(promises) which returns a new Promise that resolves when all of the Promises in the array promises themselves resolve. The returned Promise will resolve to an array of values: one for each entry in promises. The resolved array in turn is an array of our users with their nationality filled in.

From there, we just chain a function that will run the grid.items callback function callback, which will finally populate the grid just like in the first code sample.

Conclusion

Now you’ve seen the power of Promises to make writing asynchronous code a little easier. Promises are available in all modern browsers, with the exception of Internet Explorer and Opera Mini.

This is the first in a series of posts about ES6, so stay tuned for more. Next time, I’ll cover the new Fetch API, which makes writing HTTP Requests a breeze.

Learn more about <vaadin-grid> and Vaadin Elements

Reinventing the Data Grid

A Data Grid is one of the most commonly used components in business apps. It must also be one of the most complex components to implement. There are many aspects that you need to get right – customizability, performance, usability, cross-platform support, just to name a few.

Vaadin has years of experience in building components like data tables and grids for business apps, and specifically, the Vaadin Elements team has been developing and maintaining Polymer-based Web Components like <vaadin-grid> for about two years now.

A year ago, I was looking into <iron-list> – a powerful list component built by the Polymer team, which helped me realize how expressive and user-friendly HTML templates can be in Web Components for defining customized, repeating structures like lists. Using declarative HTML is a natural way for any web developer to define contents of tables as well. So, I started to think if templates could be used to build a data grid element.

At the end of January 2016, I started experimenting and building prototypes around <iron-list>. The main goal was to validate two things: what is the user’s preference in using templates over the existing patterns and how could the column templates be technically implemented so that people would be able to style and access the contents of the cells while keeping the internal structure of the table encapsulated inside a shadow root.

As a result of the experiments, <iron-data-table> was produced. During the following months, it quickly gained many of the features you usually expect to find in a data grid. As the template approach kept gathering positive feedback, I wanted to introduce the idea to my colleagues at Vaadin.

After discussing our options, we decided to implement the <vaadin-grid>2.0 based on templates.

.     .    .

Today, with the lessons learned from <iron-data-table> and with the help of the whole Vaadin Elements team, we are really moving forwards in getting <vaadin-grid> 2.0 ready for release.

Almost a year after the first experiments with the templates, we are really excited to release the first alpha version of <vaadin-grid> 2.0.

Feature-wise the alpha is in most parts already more advanced than its predecessor, but there are still some missing pieces we will implement before entering the beta stage early next year.

Here are the highlights of some of the most important features — you can take a look at the release notes and live demos for more details on v2.0.0-alpha1.

 

Extensive Customizability

Templates

Templates allow you to declaratively define how property values are displayed, to have dynamic content based on data, edit values using two-way binding and add event listeners to any element inside the cell.

More specifically, the templates in <vaadin-grid> allow you to change the selection state of items, display item details, and edit data items easily.

 

See the live templating examples for more details.

Theming

In addition to having full control over the contents of the cells and their styles, you can also use a variety of different CSS mixins to apply custom styles to the cells to make the grid fit in nicely with the design of your app.

There are different mixins targeted for the header, body and footer cells and also, there are mixins for the body cells having different states like selected or active.

Here are some examples to give you an idea of how versatile the mixins are:

Material Design

Valo

Dark Theme

.     .    .

See the live styling examples for more details.

 

Powerful Data Rendering

As <vaadin-grid> is based on <iron-list>, it uses a similar virtual DOM structure to render and reuse row and cell elements. In practice, the number of elements in the DOM is not increased by the number of items bound to the grid.

We’ve paid extra attention to making sure the scrolling experience is silky smooth in all major browsers and mobile devices and continue working on improving it.

The rendering engine has been extended to also support lazy loading by introducing the function dataSource, which you might already be familiar with from the first generation of<vaadin-grid>. The function data source allows you to feed items from a remote service, asynchronously, or for example from a generator function.

One unique feature of the improved rendering and scrolling engine is the fact that there in practice is no upper limit for the number of items.

 

 

Empowering Helper Elements

Although the templates are powerful and allow you to do virtually anything inside the cells, there are some commonly used patterns that can be quite complex to implement. We recommend encapsulating any repeating pattern into a reusable custom element.

We are currently shipping <vaadin-grid> with helper elements for providing multi-selection, filtering, and sorting.

 


.     .    .

See the live filtering and sorting examples for more details.

 

What’s next?

The focus at the beginning of 2017 will be on adding missing features like keyboard navigation, accessibility support, column reordering and resizing.

The current plan is to release the 2.0 stable as a Polymer 1.x element, but a Polymer 2.0 compatible element will also be released later.

We are also going to publish articles about the interesting things we have learned and will learn while building Vaadin Elements — topics including at least Polymer’s Templatizer, custom overlays, accessibility and testing.

As always, all feedback is highly appreciated – the fastest ways to get in touch with us are through Gitter or GitHub. You can also follow our progress in our Waffle board.

 

Originally published in medium.com

Customer Sponsored Vaadin TreeGrid Released

We are very pleased to announce the release of the Vaadin TreeGrid add-on, which is now freely available in Vaadin Directory. Similar to the TreeTable extension to Table, Vaadin TreeGrid allows users to edit and present hierarchical data sources in the Grid component.

 

The development of the add-on has been sponsored by our long-term customer Magnolia International Ltd., the company behind Magnolia CMS, an open-source Web Content Management System (CMS). Magnolia focuses on an intuitive user experience for enterprise-scale systems and thus a TreeGrid component was imperative for their users. Today this component is release to the entire Vaadin open source community.

 

Vaadin TreeGrid features:

  • Vaadin TreeGrid is an official Vaadin add-on and is fully maintained by our experts.

  • Supports custom renderers on hierarchy columns, can now be set programmatically.

  • Supports custom collapse logic as well as custom depth calculation logic.

  • Hierarchy indicators are fully configurable in CSS.

  • Supports Keyboard navigation (Alt/Option + Arrow keys).

 

Download the Vaadin TreeGrid add-on now