Close

Free Vaadin 8 Certification Week Digest

The Free Vaadin 8 Certification week officially ended on Sunday evening last week. During this past week over 1250 developers from our community signed up for the certification and tried to pass the examination. This was the strongest attendance during our free trial periods in the history of Vaadin!

40 questions from Server-side application basics over Components, to Data models and Themes had to be answered by the attendees and we are honored to see that over 270 people passed the Vaadin 8 Certification. Congratulations to all, you have truly proven your skills and expertise! As a reward we’d like to invite all of you to our 2017 Vaadin Dev Days.

If also you would like to take our examination and show your community, potential employers, and customers that you have the skills to be a true Vaadin Expert, then you can do this any time on our webiste. Try it out now or take part in our training programmes to learn, improve your skills and ensure that you’ll pass during your first try.

 

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

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?

Responsive Design Made Easy with CSS Grid and Web Components

A simple responsive layout example without any tricks nor manipulating DOM with JavaScript.

Responsive design is used to create good user interfaces across all display sizes and devices. Generally, this means the size, position, and visibility of each section within the UI is dependent on the display size. Building responsive layouts with CSS grid allows us to easily define a grid, element positions and sizes within the grid for each breakpoint we define.

Normally if we wanted to reposition an element we would use Javascript to manipulate the DOM structure, removing and re-injecting the target element wherever we want it. With CSS grid, we can simply use CSS to tell the grid where elements should be positioned. This can be especially useful when elements must be repositioned based on the display/device size.

In order to demonstrate, let's walk through an example. Let's say we need to build the UI for a web email application. We have the mock-ups for small, medium, and large screens.

Small Displays:
Mockup for Small Display

Medium Displays:
Mockup for Medium Display

Large Displays:
Mockup for Large Display

We can start out by identifying the main sections of the UI.

  • header
  • navigation
  • email list
  • preview panel
  • footer

Now we can lay out our basic elements in the DOM. Anyone looking at this could easily see what the different parts of the UI are.

<!-- header -->
<header></header>

<!-- navigation -->
<nav></nav>

<!-- list of emails -->
<main></main>

<!-- preview panel -->
<aside></aside>

<!-- footer -->
<footer></footer>

Next, we need to figure out how to display these sections on different screen sizes. For this example, lets focus on small, medium, and large size screens.

We need to draw a grid that will encapsulate all of the sections of the UI. Below you can see the grid traced by the thick black lines. The text inside each grid cell is the "grid-area" name. This name is how we can refer to sections of the grid. A grid-area can take up multiple cells.

Look at the medium display example. Notice how there are two cells that are labeled "header". This is because our header grid-area takes up both of those cells. The grid-area defines and names a portion of the grid. By having this, we can simply use CSS tell the <header> element to be placed in the "header" grid-area. We could even tell the <header> element to be positioned in the "footer" grid-area and you would see your <header> element at the bottom! This is handy because it means we can change the ordering and position and size of our HTML elements with just a single CSS rule!

The CSS code almost looks identical to our visual image. Using the grid-template-areas CSS rule, we can define our grid-areas and where what cells they belong to. Below we see the visual layout right next to the CSS.

A common use case is to move, scale, or reposition elements on different screen sizes. We can use media queries and the grid-template-areas CSS attribute to set the grid-area size and positions. For this application we want the navigation to be at the top of small displays, but on larger displays, it should be on the side. We also want the preview panel to display next to the email list on wider displays. We can easily make this position by rearranging the grid-areas using the grid-template-areas CSS rule.

Define the grid areas and how they are positioned in CSS:

/* Small Screens */

.grid-container {
    display: grid;
    grid-template-areas: 
        "header" 
        "nav" 
        "main" 
        "subcontent" 
        "footer";
}

/* Medium Screens */

@media only screen and (min-width: 768px) {
    .grid-container {
        grid-template-areas: 
            "header   header" 
            "nav      main" 
            "nav      subcontent" 
            "footer   footer";
    }
}

/* Large Screens */

@media only screen and (min-width: 1200px) {
    .grid-container {
        grid-template-areas: 
            "header  header     header" 
            "nav     main       subcontent" 
            "footer  footer     footer";
    }
}

Now that we have defined how we want the grid-areas to be positioned, we need to associate our page elements with specific grid-areas. An element can specify 'grid-area' and the name of the grid-area. This will place the element into that grid-area in the layout. Note that in this example most of the element names are the same as the grid-area name. This is not required and is just to simplify the example.

header {
    grid-area: header;
}

nav {
    grid-area: nav;
}

main {
    grid-area: main;
}

aside {
    grid-area: subcontent;
}

footer {
    grid-area: footer;
}

One thing you'll notice if you use the code above is that the heights and widths of the grid are not how we want them. We need to specify the height for each row, and the width of each column.

  • Row height is specified using the grid-template-rows CSS rule
  • Column width is specified using the grid-template-rows CSS rule
/* Small Screens */

.grid-container {
    display: grid;
    /* sets the height of each row */
    grid-template-rows: 50px 50px auto 1fr 30px;
    /* defines the grid areas */
    grid-template-areas: 
        "header" 
        "nav" 
        "main" 
        "footer";
}

/* Medium Screens */

@media only screen and (min-width: 768px) {
    .grid-container {
        /* sets the width of each column */
        grid-template-columns: 200px 1fr;
        /* sets the height of each row */
        grid-template-rows: 50px 1fr 1fr 30px;
        /* defines the grid areas */
        grid-template-areas: 
            "header   header" 
            "nav      main" 
            "footer   footer";
    }
}

/* Large Screens */

@media only screen and (min-width: 1200px) {
    .grid-container {
        /* sets the width of each column */
        grid-template-columns: 200px 1fr 1fr;
        /* sets the height of each row */
        grid-template-rows: 50px 1fr 30px;
        /* defines the grid areas */
        grid-template-areas: 
            "header  header     header" 
            "nav     main       subcontent" 
            "footer  footer     footer";
    }
}

As you can see, building responsive layouts with CSS Grid is that easy. Visit this GitHub repo for the full source code to this mail application UI.

Further reading that might interest you:

Learn more about Vaadin Elements UI components for building great web apps

A Web Architects Guide to the Future of Business Web Apps