Advanced UI components

Overview

Flavour provides some custom UI components to solve common tasks. They are optimized to work with Twitter Bootstrap, so class names generated by these components are compatible with Bootstrap. If you don’t use Bootstrap, you may write your CSS which add styles to corresponding classes. The easiest way to learn which classes you need is to apply components and examine DOM in your browser’s dev tools, or simply look through the source code of the components.

Additionally, Flavour uses its own classes for some components. It also comes with predefined CSS, which is optional.

For quick start you may simply include following lines to your HTML:

<link rel="stylesheet"
   href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" 
   integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" 
   crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="flavour/widgets.css">

Advanced components packages are not included by default, so you should use following processing instruction in your templates:

<?use w:org.teavm.flavour.widgets?>

Calendar

HTML 5 introduces different input types, including date. However, sometimes you may want to polyfill this component, in order to get more customizable styles, to change date format, and so forth. Flavour provides two components to edit date fields.

First one is calendar that insert calendar into web page:

<w:calendar value="expression" onchange="action" locale="expression" />

where

  • value="expression" specifies Date value which is displayed by the component.
  • onchange="action" is optional attribute that specifies change handler.
  • locale="expression" is optional String attribute that specifies locale. Calendar appearance may differ in different locales, for example, highlight weekdays with respect to given country.

Second one is date attribute that adds drop-down calendar to input field:

<input type="text" w:date="{ format: expression, locale: expression }">

Modal dialog

To show view object as a modal dialog, you can use Popup.showModal method. This method accepts view object that additionally implements PopupContent. PopupContent requires to implement single method called setDelegate. When Flavour creates modal window, it passes its representative to setDelegate, so that modal view could interact with popup window, for example, close it.

Note that unlike most JavaScript frameworks, Popup.showModal exits after the dialog been closed, either by user, who pushed close button, or programmatically via PopupDelegate.close. This behaviour is similar to most desktop UI frameworks, like Swing and SWT.

Running actions in background

TeaVM allows to run background actions in Thread. However, Flavour does not know anything about threads and won’t update DOM if thread updates state of view objects. Instead, you should use BackgroundWorker class that:

  • updates templates after background action gets finished;
  • provides busy state, that indicates whether BackgroundWorker is running any actions.

Example:

BackgroundWorker worker = new BackgroundWorker();
worker.run(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
})

You can monitor worker’s state by calling isBusy() method. It’s useful to expose isBusy() state to view objects so that template could disable corresponding UI elements until action completes.

Paginator

Flavour provides API that allows to show small parts of a large collection, page-by-page. This API includes several parts. First, you should implement DataSource interface, for example:

public class SimpleDataSource<T> implements DataSource<T> {
    private List<T> list;

    public SimpleDataSource(List<T> list) {
        this.list = list;
    }

    @Override
    public List<T> fetch(int offset, int limit) {
        if (offset >= list.size()) {
            return Collections.emptyList();
        }
        return new ArrayList<>(list.subList(offset, Math.min(list.size(), offset + limit)));
    }

    @Override
    public int count() {
        return list.size();
    }
}

The real-world implementation may communicate server via REST API, and server, in turn, may query data from database (in case of SQL database offset and limit parameters map directly to corresponding SQL query clauses).

Second, create PagedCursor instance, for example:

PagedCursor<String> cursor = new PagedCursor<>(new SimpleDataSource<>(Arrays.asList("foo", "bar")));

Third, bind this cursor to DOM:

<ul>
  <std:foreach var="item" in="cursor">
    <html:text value="item"/>
  </std:foreach>
</ul>

Finally, bind the same cursor to paginator component. It has the following syntax:

<w:paginator data="expression" max-pages="expression" page-link="(pageNumber, consumer) -> expression"/>

Where

  • data="expression" is a cursor that displays data.
  • max-pages="expression" is an optional expression that specifies the maximum number of pages that will be shown by the component.
  • page-link="(pageNumber, consumer) -> expression" is an optional lambda expression that generates link to page. See html:link component for description of consumer parameter. It this attribute presents, paginator will update current URL as users clicks the page button.

Note that actually paginator accepts Pageable implementation as a data source, so you may implement Pageable yourself rather than using PagedCursor.


Improve this page