Blog

Mission RIP Table: Migrate to Grid! - Data and Structure

By  
A.Mahdy AbdelAziz
·
On Sep 12, 2017 1:37:00 PM
·

This is a series of tutorials for upgrading the Table component to Grid. It will be performed by migrating to Framework 7 style of Grid, then migrating to Framework 8. As a reference, Gridv7 is the Grid component from Framework 7, while Grid is the latest Grid component in Framework 8. Naturally, you can also migrate straight from Table to the Framework 8 style Grid.

In this post we will cover some miscellaneous operations that we can perform on Grid.

Reverse by Index

This is a simple example showing how to programmatically change the order of a specific column.

Migrate from Table to Framework 7 Grid

In the old Grid, the two lines:

  table.setSortContainerPropertyId("index");
  table.setSortAscending(true);

Got merged into one single line:

  table.sort("index", SortDirection.ASCENDING);

Note that even though the two grids are using the same data, we can not share the same datasource because the sorting is happening on the container level, and both grids will be updated if they have the same datasource and we apply sort on either of them.

Related Commit: Reverse by Index: To Grid v7

Migrate to Framework 8 Grid

But in the new Grid you can! Multiple Grids can share the same list of items without any interference from how they are displayed in each Grid. So instead of defining two containers in this little example, we can simply share the same generated list:

  List<Integer> items =
    IntStream.rangeClosed(0, 4).boxed().collect(Collectors.toList());

Related Commit: Reverse by Index: To Grid v8

Row Headers

Row header was some kind of special column that places a caption for each row. It’s hidden by default but you can customize it to show an index, an item, an icon ..etc. The concept disappeared in Grid but we can still do the same and even more!

Migrate from Table to Framework 7 Grid

We can simply add the rowHeaders as a new column at the beginning of the table.

In Table you could control the visibility of columns using:

 table.setVisibleColumns("name");

But in Grid, this is done by:

  table.setColumns("icon", "name");

And here I added the generated column icon as a new column on the left side, and that's it. I also had to specify its renderer:

  table.getColumn("icon").setRenderer(new ImageRenderer());

We can do one more thing, which is basically freezing some columns to make them key or the header of the Grid. In our example here, I will freeze the first column:

  table.setFrozenColumnCount(1);

Since there is no longer a style for rowheader, I made a small modification in the style definition from:

  .v-table-cell-content-rowheader {
    background-color: gray;
  }

To:

  .rowheaders td:first-child {
    background-color: gray;
  }

This might be a quick workaround, in a future post we will discuss a better way of doing this using StyleGenerator.

Note that I also gave the Grid a stylename, to distinguish it from other Grid examples and not apply the same style of the first column on all of them:

  table.addStyleName("rowheaders");

Related Commit: Row Headers: To Grid v7

Migrate to Framework 8 Grid

Things get easier and simpler for the new Grid. We will see that code gets much shorter and cleaner. We get rid of Container and the GeneratedPropertyContainer. Here is how columns are created, quickly and straightforward:

  table.addColumn(c ->
    new ThemeResource( ... ))
    .setRenderer(new ImageRenderer<>());
  table.addColumn(Planet::getName).setCaption("Name");

Since we are using a proper bean in this example, the Name column is generated safely without any need of a hardcoded string definition of a propertyId.

Related Commit: Row Headers: To Grid v8

Context Menu

Starting Vaadin Framework 7.6, there is an official Vaadin add-on called ContextMenu that makes life easier when dealing with context menu. In this example, I will replace all custom context menu code written especially for Table, by the add-on. So first of all, let’s add the maven dependency:

  <dependency>
    <groupId>com.vaadin</groupId>
    <artifactId>vaadin-context-menu</artifactId>
    <version>2.0.0</version>
  </dependency>

Migrate from Table to Framework 7 Grid

Dealing with the ContextMenu add-on is quite easy, instead of a custom handler in the table with lengthy definition of menu items and their click events:

  table.addActionHandler(new Handler() {
    ...
  }

We simply define the ContextMenu items and their event handling directly in one line, for example:

  ctxMenu.addItem("Delete Item", evt -> {
    table.getContainerDataSource().removeItem(table.getSelectedRow());
  });

Related Commit: Context Menu: To Grid v7.6+

Migrate to Framework 8 Grid

You can not add or remove items from the middle of the container as it was the case with containers:

  table.getContainerDataSource().removeItem(selectedItem);

You must update the backend, regenerate the items, and add them in the grid. In the example here, I will ignore the backend update and update the items directly from the local list:

  scientists.remove(selectedItem);
  table.setItems(scientists);

Related Commit: Context Menu: To Grid v8

 

You can do a lot more things with the ContextMenu add-on to make it more fancy with icons, and more functionalities with submenus if needed, check out this post for a few more examples.

Filtering

This is one of those examples that Grid implements in a much easier and faster way, we will see a huge code improvement in the old Grid, and even better in the newer Grid.

Migrate from Table to Framework 7 Grid

The implementation was a bit cumbersome in Table, but in Grid we can simply add one extra header for the filtering. In this extra row, we add the filtering components, let’s say, TextField, and finally we get an instant filtering result as we type. So remove all this code:

  table.setTableFieldFactory(new DefaultFieldFactory() {
    ...
  }

Replace with:

  Gridv7.HeaderRow filterRow = table.appendHeaderRow();
  filterRow.getCell("Name").setComponent(nameFilter);
  filterRow.getCell("Born").setComponent(bornFilter);

And a value change listener on each TextField to apply the filtration instantly:

  nameFilter.addValueChangeListener(evt -> {
    // Remove old filter
    container.removeContainerFilters("Name");
    // Set new filter
    container.addContainerFilter(
      new SimpleStringFilter("Name", evt.getValue(), true, false));
  });

Related Commit: Filtering: To Grid v7

Migrate to Framework 8 Grid

And things get even more easy with the new Grid. One way of implementing this, is to apply filters directly on the DataProvider:

  listDataProvider.setFilter(o ->
    String.valueOf(o[0]).toLowerCase().contains(nameFilterTxt.toLowerCase()) &&
    String.valueOf(o[1]).contains(bornFilterTxt)
  );

Note that I don’t need to clear filters anymore because it’s a global setting, not sequential add.

Related Commit: Filtering: To Grid v8

Scroll to Item

I came across an interesting discussion about this topic, generated from those migration posts. So unless you want to perform a traditional scrolling to a specific row index, I’d recommend checking the forum post as well.

Migrate from Table to Framework 7 Grid

The migration is simple, just a change in the API name from:

  table.setCurrentPageFirstItemId(itemId);

To:

  table.scrollTo(itemId);

Related Commit: Scroll: To Grid v7

Migrate to Framework 8 Grid

Nothing different here, except that I’d like to stress one more time that we can only scroll to a specific row index:

  table.scrollTo(rowIndex);

Otherwise, you can use my temporary workaround to navigate to a specific object in the Grid:

  grid.scrollTo(
    new ArrayList(
      ((ListDataProvider) grid.getDataProvider()).getItems()
    ).indexOf(obj)
  );

Related Commit: Scroll: To Grid v8

Remove all Items

Migrate from Table to Framework 7 Grid

In this example I did not need to change anything except changing the definition from new Table to new Gridv7 and that’s it! The API is exactly the same:

  container.removeAllItems();

Related Commit: Remove all: To Grid v7

Migrate to Framework 8 Grid

One way of clearing the new Grid is to just give it an empty list:

  table.setItems( Collections.emptyList());

Related Commit: Remove all: To Grid v8

Buffering

In this example we are modifying the data from one Grid, and only updating it once the save button is clicked.

Migrate from Table to Framework 7 Grid

A better way of implementing this, is to simply use GridEditor. By default it’s Buffered but we will see that if we disable Buffering, the built-in save button will disappear and we will have to handle this manually:

  editable.setEditorBuffered(false);

And committing changes can be done on editor level:

  editable.getEditorFieldGroup().commit();

Related Commit: Buffering: To Grid v7

Migrate to Framework 8 Grid

In the new Grid, there are a little bit more details to take care of. In the editor, you have to specify the components, and binding, and converter if needed, along with error messages.

  editable.addColumn(Bean::getEnergy)
    .setEditorBinding(editable.getEditor().getBinder()
    .forField(new TextField())
    .withConverter(new StringToDoubleConverter("Must be double"))
    .bind(Bean::getEnergy, Bean::setEnergy));

We do not need to have a separate Save button, since this is implemented already in the GridEditor. If buffered (default), the save appears under the row, otherwise when unbuffered then changes go immediately to the datasource. I’ll keep the button though to refresh the other Grid to show the modifications:

  rotable.getDataProvider().refreshAll();

Related Commit: Buffering: To Grid v8

Long Table

Migrate from Table to Framework 7 Grid

This example shows one of the new features of Grid that can’t be seen visually, but only through benchmarks and measures. Handling a large amount of data in Grid is very efficient, especially on the client side. So migration here will be simply by removing all Table optimization hacks and letting the Grid do the magic!

Related Commit: Long Table: To Grid v7

Migrate to Framework 8 Grid

In the new Grid, benchmarks not only show improvements on the client side, but on the server side as well, and memory footprint.

Related Commit: Long Table: To Grid v8

 

Have you got more examples to share with the community, that are not covered here?

A.Mahdy AbdelAziz
AMahdy is an international technical speaker, Google developer expert (GDE), trainer and developer advocate. Passionate about Web and Mobile apps development, including PWA, offline-first design, in-browser database, and cross platform tools. Also interested in Android internals such as building custom ROMs and customize AOSP for embedded devices. AMahdy.net
Other posts by A.Mahdy AbdelAziz