Close

Making custom components for declarative use

Vaadin introduced declarative syntax for initializing screen compositions in Framework 7.4. Since its release, declarative serialization and initialization have been associated for the most part with the Vaadin Designer product, where this syntax is used to describe a user interface design in a compact, procedure-free format.

Unlike other rich component frameworks like JavaFX and Microsoft’s WPF, the mapping in Vaadin of declarative attributes to component properties is both implicit and explicit, and customization is procedural. The explicit mapping mechanism is available and free to use for all developers who are building their own components.

In this article we will review how you can author your own custom Vaadin components and control the implicit and explicit mappings, and describe the challenges of attribute discoverability and how to circumnavigate these.

Why you should do this

Supporting declarative is not actually a requirement for uploading add-ons to the Vaadin Directory or for sharing custom Vaadin components within your organization. However, not supporting it (or worse, inadvertently designing the component to break on declarative use) limits its usability and makes any use in the Vaadin Designer impossible.

The constructive reason to do this of course is to reap the modelling benefits of declarative UIs, many of which can be yours regardless if you use Vaadin Designer or not. A Vaadin Designer license is not required to develop the declarative behavior of your components; testing and simulation can be achieved moreover within simple procedural patterns.

Define the class

To understand how to make your component class declarative-ready, it helps to understand the responsibilities of the class when declarative syntax is read and written. Unlike MXML and XAML, Vaadin declarative HTML is not compiled during build but parsed and interpreted at runtime.

Declarative initialization is a shared responsibility between the Vaadin framework and the individual UI components. At runtime this format is processed in the framework by classes in the com.vaadin.ui.declarative package to transform the HTML into a sequence of constructor invocations. Subsequently, responsibility shifts to the individual components to process the attributes of the HTML element with which they are associated.

At design-time, the Vaadin Designer also reads and writes the declarative format, and like with the declarative classes delegates to the component the reading and writing of attributes and their mapping to the component’s state.

The most common problems you will encounter when starting with declarative readiness are related to the accessibility of your component to participate in these collaborations. If you have created a component with only a procedural use in mind, these problems won’t necessarily appear.

Your custom component class must have public accessibility
Since the Vaadin framework and the Vaadin Designer have different packages, default accessibility is not an option.

Your class must not be in the default package
In Vaadin, declarative name resolution assumes a named package prefix separated by a dash, as in vaadin-button.

Your class must be a top-level class
Not an inner or member class

Your class must be instantiatable through a public no-arg constructor
Since the responsibilities of initialization lie with the component, the framework and Designer both need to instantiate an object which can take over the initialization of itself.

Define the attribute handling

Once you’ve got the essentials in place for Vaadin tools to create your components, you need to think about how your components will serialize and deserialize their state in a list of attributes.

Attribute handling happens in two phases during initialization. There is a specific terminology to describe which properties are handled in which phase, which we will explain in the next paragraphs. In summary, in the first phase all implicit initialization takes place; and in the second phase properties that require custom initialization are initialized following explicitly coded custom logic. The good news is component authors can control which properties fall into both categories.

To summarize with API-specific terminology, in the first phase there is the processing of supported attributes that are marked as default attributes. In a second phase, there is the processing of the rest of the attributes, which include supported, custom attributes, and unsupported attributes.

First phase: Implicit initialization
First let’s review the distinction between supported and unsupported attributes. The notion of supported attributes is codified in the static getSupportedAttributes method of com.vaadin.ui.declarative.DesignAttributeHandler. This method returns a Collection of Strings listing the attribute names that are suitable for implicit declarative initialization for a particular class.

Supported attributes involve properties that are uncomplicated to convert into a String format and can be handled implicitly by the Vaadin declarative libraries. There is nothing you as author can do to broaden the definition of what counts as “supported”. There are a number of criteria for these properties:

  1. Getter must match a setter following regular JavaBeans naming conventions
  2. Getter and setter must be public
  3. The type of the property must be one of:
    BigDecimal boolean Boolean byte Byte char Character Date double
    Double float Float integer Integer LocalDate LocalDateTime long
    Long Resource short Short ShortcutAction String TimeZone

If these three criteria are met then the Vaadin framework declarative libraries will provide implicit, default support for the serialization and no effort is required from the component author. These properties will be supported, and if the component author does not take any steps to interfere with the implicit serialization the attributes will be “supported” and “default”.

While the component author has no means to influence the behavior of the DesignAttributeHandler, they can prevent implicit handling of specific supported attributes. To do this the component must override a method from the AbstractComponent class called getCustomAttributes. Here is an example in which we prevent implicit initialization of an attribute called “placeholder”:

@Override
protected Collection<String> getCustomAttributes() {
  Collection<String> retval = super.getCustomAttributes();
  retval.add("placeholder");
  return retval;
}

Once this method has been added to a custom component, the placeholder property will be ignored during implicit serialization and initialization. In the API-specific terminology, by adding “placeholder” to the list of “custom attributes”, the attribute will be removed from the list of “default attributes”.

Second phase: Explicit initialization
Any attribute that is not “supported” or not “default” must be explicitly covered by custom attribute handling or be ignored. To implement this behavior, you override the methods readDesign and writeDesign that are defined in the Component interface. These methods are implemented in pairs, because your component should only write things that it can read again at a later date.

Under the hood, Vaadin uses jsoup to read all declarative designs, and the APIs that connect your components to their attributes represent the state as a org.jsoup.nodes.Element. Explicit attribute handling works by processing jsoup Elements returned from the declarative APIs, which are only provided to your component after all attributes have been parsed. These attributes are accessible through the Element as a LinkedHashMap that preserves the order in which they appeared in the HTML.

This means as a component author you have unusual flexibility to determine in which order you process these attributes. You could impose a fixed order, respect the order as they appear in the design, or retrieve them based on some dynamically configured ordering.

For example, consider a Table component that has a fixedColumns property - you might have validation in place to ensure that the number of fixed columns you set in your table is not larger than the total number of columns. Even though a property like fixedColumns might be a simple int that could arguably be handled implicitly, this would be a typical case for handling explicitly: You would want to ensure that the cols property was initialized before the fixedColumns property.

In the following examples we will create a custom component that contains two properties: a radiance property of type int and a weighted property of type boolean. To enable explicit initialization for these two properties we will override the inherited readDesign and writeDesign methods.

Here is a simplistic sample implementation of readDesign that initializes the properties of its object in the order in which they appeared in the design:

@Override
public void readDesign(Element design, DesignContext dc) {
  super.readDesign(design, dc);
  for (Attribute at : design.attributes()) {
    String s = at.getValue();
    switch (at.getKey()) {
      case "radiance":
        setRadiance(Integer.valueOf(s));
        break;
      case "weighted":
        setWeighted(Boolean.valueOf(s));
    }
  }
}

And for achieving the same but imposing a fixed ordering of the initialization:

@Override
public void readDesign(Element design, DesignContext dc) {
  super.readDesign(design, dc);
  if (design.hasAttr("radiance")) {
    setRadiance(Integer.valueOf(design.attr("radiance")));
  }
  if (design.hasAttr("weighted")) {
    setWeighted(Boolean.valueOf(design.attr("weighted")));
  }
}

For serializing the component state into Vaadin declarative HTML, the writeDesign method exists with a very similar signature:

@Override
public void writeDesign(Element design, DesignContext dc) {
  super.writeDesign(design, dc);
  design.attr("radiance", String.valueOf(getRadiance()));
  design.attr("weighted", String.valueOf(isWeighted()));
}

Note in the above examples we have taken shortcuts with regard to the formatting of properties for the sake of illustration and simplicity. For a safer implementation and also to have a consistent behavior for handling attributes equal to their default values (0 for int properties, false for boolean...) consider using the helper methods from the com.vaadin.ui.declarative.DesignAttributeHandler class. There you find static readAttribute and writeAttribute methods which you can use in your implementations of readDesign and writeDesign.

Testing your component

Two facets of Vaadin declarative HTML are useful for component testing.

First, the processing of Vaadin declarative statements is always geared to a root. This is a natural fit for designs of complete user interfaces which need to get set as content to a UI: UI’s setContent method expects a tree root as argument, and the Design.read method returns the root of a tree as a Component. But there is no limitation on the number of objects that can be contained in the tree, and for the Design.read method there is no requirement that the root Component actually inherit from AbstractComponentContainer or contain any children.

Second, Vaadin’s declarative syntax is not XML but HTML, and namespace resolution of package names follows custom logic. This means a valid declarative design (especially for items in the vaadin default namespace) can be tiny as several properties can be initialized in a single string.

Taking these two together, it’s very easy to do spot instantiations of visual components inside the procedural code of the view. For example:

TextField name = (TextField)Design.read(
  new java.io.StringBufferInputStream(
    "<vaadin-text-field caption='name' maxlength='8' " +
    "placeholder='enter name' />"));

You can take advantage of the easy integration of Vaadin declarative HTML inside your code to help you test your components. In the example of our component, one unit test could look as follows:

Steamer steamerA = new Steamer();
steamerA.setWeighted(true);
steamerA.setRadiance(3375);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Design.write(steamerA, bos);
String steamerDeclara = bos.toString("UTF-8");
ByteArrayInputStream bis = 
    new ByteArrayInputStream(steamerDeclara.getBytes("UTF-8"));
Steamer steamerB = (Steamer)Design.read(bis);
Assert.assertEquals(steamerA,  steamerB);

Programming discoverability

At the moment, discovering which attributes your component supports using reflection and introspection is limited to what can be found by the getSupportedAttributes and getCustomAttributes methods discussed in the sections above. This severely limits your options for variable length properties like collections or arrays; or properties of custom reference types.

To support a variable length property in a discoverable attribute, you are limited to custom encoding of the value in Strings. String is the only supported type that is inherently variable length.

For example, consider your component has a property of type int[] called yearsActive. Even if you had a getYearsActive and setYearsActive method, years-active would never be a supported attribute due to its underlying type. You could work around that by implementing an additional JavaBeans style get/set pair representing a new property of type String that performed conversion to a private field of type int[]:

private int[] yearsActive;
public int[] getYearsActive() {
  return this.yearsActive;
}
public void setYearsActive(int[] arg) {
  this.yearsActive = arg;
}
public String getYearsActiveAsString() {
  String retval = "";
  retval = Arrays.toString(yearsActive);
  return retval;
}
public void setYearsActiveAsString(String arg) {
  yearsActive = 
    Arrays.stream(arg.substring(1, arg.length()-1).split(","))
      .map(String::trim).mapToInt(Integer::parseInt).toArray();
}

With just these two additional methods, a years-active-as-string attribute would become available and used implicitly during serialization and initialization.

The approach for custom reference type properties would require a similar workaround. You could have a String conversion getter and setter, or you could implement new property patterns for each relevant primitive field in the object.

.

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?