Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Table is Dead, Long Live the Grid

DZone's Guide to

The Table is Dead, Long Live the Grid

· DevOps Zone
Free Resource

Download the blueprint that can take a company of any maturity level all the way up to enterprise-scale continuous delivery using a combination of Automic Release Automation, Automic’s 20+ years of business automation experience, and the proven tools and practices the company is already leveraging.

Vaadin 7.4.0 is finally out and with it comes the long awaited successor of the Table component, the Grid. As for the rest of Vaadin, it's quite well-documented.

This article aims at showing a simple example of this new component to highlights differences with the previous Table. In order to do so, let's use an existing sample - namely my Vaadin workshop that displays a table of messages, with date, author, text and a delete button.


First steps with Grid

A first draft implementation would look like this:

public class SampleGrid extends Grid {

    public SampleGrid(Container.Indexed indexed) {
        setContainerDataSource(indexed);
    }
}

Grid's columns

It's simple and quite straightforward. At this point, nothing distinguishes the new Grid from the old Table, only default sortable columns. As most chat applications don't allow that - and the one from the workshop neither, let's disable it:

public class SampleGrid extends Grid {

    public SampleGrid(Container.Indexed indexed) {
        setContainerDataSource(indexed);
        getColumns().stream().forEach(c -> c.setSortable(false));
    }
}

So the Grid component has the concept of columns. One can get a reference on a single column column by passing the propertyId of the underlying item with getColumn(propertyId) or the sequence of all columns with getColumns(). Note that Java 8 helps with the handling of each column with the Stream API and a simple lambda's usage.

Wrapped container

The next step is to hide the message's id column; with Table, it was achieved by callingsetVisibleColumns() and omitting the id property in the parameter array. With Vaadin 7.4, theContainer hierarchy has been enriched with the Delegate pattern: the GeneratedPropertyContainer is a wrapper around another container but offers additional methods, includingremoveContainerProperty(propertyId) that effectively hides a property from the wrapped container. This produces the following code:

public class SampleGrid extends Grid {

    public SampleGrid(Container.Indexed indexed) {
        GeneratedPropertyContainer wrapperContainer = new GeneratedPropertyContainer(indexed);
        wrapperContainer.removeContainerProperty("id");
        setContainerDataSource(wrapperContainer);
        getColumns().stream().forEach(c -> c.setSortable(false));
    }
}

Renderers

Now, let's format the date column. With Table, it required implementing a dedicated ColumnGenerator. At first glance, it seems using the addGeneratedProperty of the wrapped container mentioned in the above section would be the thing to do. However, for simple formatting issues, Vaadin 7.4 provides a Rendererinterface: any column can be set such a renderer. Icing on the cake, a DateRenderer is provided that accepts a format string as an argument.

public class SampleGrid extends Grid {

    private static final String FORMAT = "%1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS";

    public SampleGrid(Container.Indexed indexed) {
        GeneratedPropertyContainer wrapperContainer = new GeneratedPropertyContainer(indexed);
        wrapperContainer.removeContainerProperty("id");
        setContainerDataSource(wrapperContainer);
        getColumn("timeStamp").setRenderer(new DateRenderer(FORMAT));
        getColumns().stream().forEach(c -> c.setSortable(false));
    }
}

Note the format string uses the pattern from Formatter, not from SimpleDateFormat.

Multi-selection

The last task is to add the delete feature. Out-of-the-box, setting the selection mode to multi create a new column with checkboxes to select multiple lines:

public class SampleGrid extends Grid {

    private static final String FORMAT = "%1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS";

    public SampleGrid(Container.Indexed indexed) {
        GeneratedPropertyContainer wrapperContainer = new GeneratedPropertyContainer(indexed);
        wrapperContainer.removeContainerProperty("id");
        setContainerDataSource(wrapperContainer);
        getColumn("timeStamp").setRenderer(new DateRenderer(FORMAT));
        getColumns().stream().forEach(c -> c.setSortable(false));
        setSelectionMode(SelectionMode.MULTI);
    }
}

Then, just create a simple button that will delete all selected lines. This gets the job done. However, this is not the original design: in the above screenshot, each line provides its own dedicated button. Pro, you have to click on the selected line so you prevent most mistakes; con, you cannot batch delete.

Along with the date renderer, Vaadin 7.4 also provides a ButtonRenderer that accepts a RenderClickListener to add behavior. This only needs to be set on the id column.

public class SampleGrid extends Grid {

    private static final String FORMAT = "%1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS";

    public SampleGrid(Container.Indexed indexed) {
        GeneratedPropertyContainer wrapperContainer = new GeneratedPropertyContainer(indexed);
        setContainerDataSource(wrapperContainer);
        getColumn("timeStamp").setRenderer(new DateRenderer(FORMAT));
        getColumn("id").setRenderer(new ButtonRenderer(event -> {
            Object itemId = event.getItemId();
            indexed.removeItem(itemId);
        }));
        getColumns().stream().forEach(c -> c.setSortable(false));
        setSelectionMode(SelectionMode.MULTI);
    }
}

The downside of this approach is that you cannot change the button's label as it is taken from the underlying object with no way of changing it. In this case, the message's id will be displayed in a button and users might cluelessly delete the message. That's out of the question.

This makes it only slightly more complex as we need to add a dedicated container property that always returns the wanted button label and assign it the renderer from above. The final code looks like the following:

public class SampleGrid extends Grid {

    private static final String FORMAT = "%1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS";

    public SampleGrid(Container.Indexed indexed) {
        GeneratedPropertyContainer wrapperContainer = new GeneratedPropertyContainer(indexed);
        wrapperContainer.removeContainerProperty("id");
        setContainerDataSource(wrapperContainer);
        wrapperContainer.addGeneratedProperty("delete", new PropertyValueGenerator<String>() {
            @Override
            public String getValue(Item item, Object itemId, Object propertyId) {
                return "Delete";
            }

            @Override
            public Class<String> getType() {
                return String.class;
            }
        });
        getColumn("delete").setRenderer(new ButtonRenderer(event -> {
            Object itemId = event.getItemId();
            indexed.removeItem(itemId);
        }));
        getColumn("timeStamp").setRenderer(new DateRenderer(FORMAT));
        getColumns().stream().forEach(c -> c.setSortable(false));
    }
}

A new generated property is added with the PropertyValueGenerator always returning the string Delete. More advanced requirements could use a Locale to have a display depending on the user preferences, but this code is enough for ours. Note the button renderer is kept but assigned to the new delete generated column instead of the id's. Finally, the multiselection mode has to be removed.

Header

The final touch is to remove the column headers as befits any chat application. This is simply done withsetHeaderVisible(false).

The final result looks like the original table, while saving some lines of code.


The code for this article can be browsed and forked on Github.

Download the ‘Practical Blueprint to Continuous Delivery’ to learn how Automic Release Automation can help you begin or continue your company’s digital transformation.

Topics:

Published at DZone with permission of Nicolas Frankel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}