Compose Views From Reusable Designs

In this tutorial, we will create a content view, with a top menu and a search bar, by using reusable components in Vaadin Designer. Then we could implement some simple logic to connect the content view and the search bar using Java code. The result will look like the figure below:

Simple search view

Prepare workspace

  1. First off, let’s start with a Project Base from https://vaadin.com/start

  2. Build the project using mvn clean package so that we get all the necessary components.

  3. Import the project into your IDE of choice. (In this tutorial, we are going to use IntelliJ IDEA 2018)

Create the top menu component

Create a new design by using New Vaadin 10 Design wizard
  1. Create a new Vaadin 10 Design via the IDE menu

  2. Select the folder src/main/webapp/frontend for the Design location

    • Optional: Select the Create Java companion file checkbox to generate a Java companion file for this component.

  3. Enter design name, for example: top-menu

  4. Press OK to create the design

Tip
In this tutorial, we will not use the top-menu component in Java code, so creating a Java companion file is an optional step.
Create top-menu design
Now, let’s add a vaadin-horizontal-layout as a root with four vaadin-button as children with the below steps
  1. Search for vaadin-horizontal-layout in the Search field of the Palette. The vaadin-horizontal-layout will appear under the group vaadin-ordered-layout.

  2. Drag and drop it into the paper or just double-click on it.

  3. Similarly, add four vaadin-button into the vaadin-horizontal-layout.

  4. Change the text content of the buttons to anything you want by selecting the text under vaadin-button in the Outline, then editing its content property in the Properties table.

    • Optional: You can set tertiary as the button’s theme property to make it look more like a menu item. You can find more themes at vaadin-button page

  5. Select the vaadin-horizontal-layout and change its style property to width: 100%; justify-content: center;. This will make our buttons stay in the center of the design.

    • Optional: Using spacing theme for vaadin-horizontal-layout will append a little bit more space between our buttons

top-menu design
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/vaadin-ordered-layout/src/vaadin-horizontal-layout.html">
<link rel="import" href="../bower_components/vaadin-button/src/vaadin-button.html">

<dom-module id="top-menu">

    <template>
      <vaadin-horizontal-layout style="width: 100%; justify-content: center;" theme="spacing">
        <vaadin-button theme="tertiary">
          Home
        </vaadin-button>
        <vaadin-button theme="tertiary">
          Products
        </vaadin-button>
        <vaadin-button theme="tertiary">
          Services
        </vaadin-button>
        <vaadin-button theme="tertiary">
          About
        </vaadin-button>
      </vaadin-horizontal-layout>
    </template>

    <script>
        class TopMenu extends Polymer.Element {
            static get is() {
                return 'top-menu';
            }
        }
        customElements.define(TopMenu.is, TopMenu);
    </script>

</dom-module>

Create the search bar component

With the same steps from the top-menu component creation, let’s create another component named search-bar and its Java companion file, as well. The search-bar component will contain two elements: search field and search button, which line up horizontally. Therefore, we can use vaadin-horizontal-layout as the root, vaadin-text-field for the search field and vaadin-button for the search button.

We can decorate the component with these steps
  1. Set style property of vaadin-horizontal-layout to width: 100%; because we don’t want the search bar to expand vertically

  2. Add spacing and padding theme for the layout to reserve some spaces between the elements and the padding from the document.

  3. Set style property of vaadin-text-field to flex-grow: 1;

  4. Set your placeholder text for the search field using placeholder property

Later in this tutorial, we might need to use the search field and the search button in Java code. Let’s export them by checking their Java checkboxes in the Outline.

The search-bar design should look like search-bar design

search-bar design
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/vaadin-text-field/src/vaadin-text-field.html">
<link rel="import" href="../bower_components/vaadin-ordered-layout/src/vaadin-horizontal-layout.html">
<link rel="import" href="../bower_components/vaadin-button/src/vaadin-button.html">

<dom-module id="search-bar">

    <template>
      <vaadin-horizontal-layout style="width: 100%;" theme="spacing padding">
        <vaadin-text-field style="flex-grow: 1;" id="vaadinTextField" placeholder="Search..."></vaadin-text-field>
        <vaadin-button id="vaadinButton">
          Search
        </vaadin-button>
      </vaadin-horizontal-layout>
    </template>

    <script>
        class SearchBar extends Polymer.Element {
            static get is() {
                return 'search-bar';
            }
        }
        customElements.define(SearchBar.is, SearchBar);
    </script>

</dom-module>

Create the content view

In the same way as above, we can create a new design called content-view along with its Java companion file ContentView.java. In this design, we will add a vaadin-vertical-layout as the root layout. After that, from the Project designs section of the Palette, we can add the top-menu and the search-bar as children of the layout.

Project designs

We also need a div and ul as the container for our search result in the view. Then our content-view structure will be like content-view design structure.

To prepare for some simple functionalities later, we should export search-bar and ul to Java.

content-view design structure
Let’s add some additional styles for the design
  1. Set top-menu style property to width: 100%;

  2. Set search-bar style property to width: 100%;

  3. Set div style property to width: 100%; flex-grow: 1;

content-view design
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="search-bar.html">
<link rel="import" href="../bower_components/vaadin-ordered-layout/src/vaadin-vertical-layout.html">
<link rel="import" href="top-menu.html">

<dom-module id="content-view">

    <template>
      <vaadin-vertical-layout style="width: 100%; height: 100%;" id="vaadinVerticalLayout">
      <top-menu style="width: 100%;"></top-menu>
      <search-bar style="width: 100%" id="searchBar"></search-bar>
      <div style="width: 100%; flex-grow: 1;">
        <ul id="ul"></ul>
      </div>
      </vaadin-vertical-layout>
    </template>

    <script>
        class ContentView extends Polymer.Element {
            static get is() {
                return 'content-view';
            }
        }
        customElements.define(ContentView.is, ContentView);
    </script>

</dom-module>

Add a route to the view

To add a route to the content-view, we need to open the Java companion file (ContentView.java) by either navigating via the project explorer, or clicking on the Java connection indicator. Then add @Route("content-view") annotation to the ContentView class.

...
@Tag("content-view")
@HtmlImport("frontend://src/content-view.html")
@Route("content-view")
public class ContentView extends PolymerTemplate<ContentView.ContentViewModel> {
...
content-view design

Add simple search functionality

Let’s add some code to set the content from search field to the content view when pressing the Search button.

@Tag("search-bar")
@HtmlImport("frontend://src/search-bar.html")
public class SearchBar extends PolymerTemplate<SearchBar.SearchBarModel> {

    @Id("vaadinTextField")
    private TextField vaadinTextField;
    @Id("vaadinButton")
    private Button vaadinButton;

    private final List<SearchBarListener> listeners;

    public interface SearchBarModel extends TemplateModel {

    }

    public SearchBar() {
        listeners = new CopyOnWriteArrayList<>();
        vaadinButton.addClickListener(buttonClickEvent -> {
            for (SearchBarListener listener :
                    listeners) {
                listener.onSearch(vaadinTextField.getValue());
            }
        });
    }

    public void addSearchListener(SearchBarListener listener) {
        listeners.add(listener);
    }

    public void removeSearchListener(SearchBarListener listener) {
        listeners.remove(listener);
    }

    @FunctionalInterface
    public interface SearchBarListener {
        void onSearch(String text);
    }
}
@Tag("content-view")
@HtmlImport("frontend://src/content-view.html")
@Route("content-view")
public class ContentView extends PolymerTemplate<ContentView.ContentViewModel> {

    @Id("ul")
    private UnorderedList ul;
    @Id("searchBar")
    private SearchBar searchBar;
    private final SearchBar.SearchBarListener searchBarListener;

    public interface ContentViewModel extends TemplateModel {

    }

    public ContentView() {
        searchBarListener = text -> ul.add(new ListItem(text));
        searchBar.addSearchListener(searchBarListener);
    }

    @Override
    protected void onDetach(DetachEvent detachEvent) {
        super.onDetach(detachEvent);
        searchBar.removeSearchListener(searchBarListener);
    }
}

It’s time to start the application and see our result by running mvn jetty:run from the project folder. Our view is available at http://localhost:8080/content-view

Final result