Basil Bourque
Basil Bourque

Reputation: 339472

Where do I put all the “guts” of my app in a Vaadin app? Keep adding to “MyUI” class?

I have successfully got my first Vaadin 7 app up and running, as created for me by the Maven archetype. This template for the app shows the layout, its widgets, and the business logic for the widget (button being clicked), all defined within the MyUI class.

Where do I go from here? Do I just keep adding stuff in that MyUI or is there a better way to build out the pieces of my app?

Here is the Java source code of the MyUI given to me by the Maven archetype.

package com.example.vaadinlayoutsexample;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.annotations.Widgetset;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

/**
 * This UI is the application entry point. A UI may either represent a browser window 
 * (or tab) or some part of a html page where a Vaadin application is embedded.
 * <p>
 * The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be 
 * overridden to add component to the user interface and initialize non-component functionality.
 */
@Theme("mytheme")
@Widgetset("com.example.vaadinlayoutsexample.MyAppWidgetset")
public class MyUI extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        final VerticalLayout layout = new VerticalLayout();

        final TextField name = new TextField();
        name.setCaption("Type your name here:");

        Button button = new Button("Click Me");
        button.addClickListener( e -> {
            layout.addComponent(new Label("Thanks " + name.getValue() 
                    + ", it works!"));
        });

        layout.addComponents(name, button);
        layout.setMargin(true);
        layout.setSpacing(true);

        setContent(layout);
    }

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet {
    }
}

Screen shot of app running.

screen shot of Vaadin app template running in a web browser window

Upvotes: 0

Views: 149

Answers (2)

nyg
nyg

Reputation: 2552

When your application starts having a lot more views, you should start using at least a Navigator as well as a ViewProvider. Both class are in the Vaadin framework.

The Navigator object would be created in the MyUI class. Each view implements the View interface, for each view you can also have a presenter (though this is not a Vaadin concept).

A custom ViewChangeListener as well as a NavigationStateManager might also be of use.

See https://vaadin.com/docs/-/part/framework/advanced/advanced-navigator.html for more details. I also have made of "base app" which uses both the MVP and DAO pattern, you can check it out here: https://github.com/nyg/vaadin-app-base.

Upvotes: 5

Basil Bourque
Basil Bourque

Reputation: 339472

The app template is purposely kept brief to make it easy to read and comprehend for a newbie. All the “moving parts” of the user interface are gathered in that one class, MyUI.

Adding more and more widgets and behavior to that MyUI quickly becomes unwieldy. There are many approaches you can take to break out your app into various pieces, as Vaadin is quite flexible in this regard. I will describe my own approach.

In real work, I keep the MyUI class as short as possible. As a subclass of UI, the MyUI represents the entire viewport of content displayed by either:

  • A web browser’s window or tab (the common way to use Vaadin)
  • The rectangle of a Portlet view (if your Vaadin app is being embedded within a web page)

In other words, the UI class is just a blank canvas that knows how to fit inside the host’s rectangular viewport. The job of the UI is to mediate between the host viewport and the Vaadin content. Managing the details of that Vaadin content is better left to a separate object, defined as subclasses of Layout. So the UI code becomes simpler, just a matter of juggling one or more Layout subclass objects.

For example, you might display a log-in Layout. Then if the user authenticates successfully you switch out that Layout. It its place you instantiate a different Layout subclass to display your app’s main content. Your MyUI code would handle the switching from a LoginLayout layout object initially to replacement with a InvoiceListingLayout layout object.

When starting a new Vaadin app, the first thing I do is extract the code for that VerticalLayout and “Click Me” button, and move it to a new Java class file. The new class is an extension of VerticalLayout, with a constructor that establishes the field, the button, and the behavior of the button.

Below is source code for that new class, ClickMeLayout. Real-world layouts have much more code, so I usually break out that constructor method into three subroutines:

  • configure()
    Set attributes of the overall layout such as calls to setSpacing and setMargins.
  • widgets()
    Establish each of the widgets to appear on this layout. I almost always assign the new widgets as private member variables on the class rather than as a local variable as seen in the original app-template code generated by Maven.
  • arrange()
    Add the widgets to the layout in a certain order and arrangement. Often means creating nested layouts as sub-groupings of widgets.

I also made explicit the this. syntax, my own preference.

Here is the new class, ClickMeLayout.java.

package com.example.vaadinlayoutsexample;

import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;

/**
 *
 * @author Basil Bourque. Use freely, but only at your own risk entirely.
 */
public class ClickMeLayout extends VerticalLayout {

    private TextField name = null;
    private Button button = null;

    public ClickMeLayout () {
        this.configure ();
        this.widgets ();
        this.arrange ();
    }

    private void configure () {
        this.setMargin ( true );
        this.setSpacing ( true );
    }

    private void widgets () {

        // “Name” field
        this.name = new TextField ();
        this.name.setCaption ( "Type your name here:" );

        // “Click Me” button
        this.button = new Button ( "Click Me" );
        this.button.addClickListener ( e -> {
            this.addComponent ( new Label ( "Thanks " + this.name.getValue () + ", it works!" ) );
        } );

    }

    private void arrange () {
        this.addComponent ( this.name );
        this.addComponent ( this.button );
    }

}

With that class established, we can go back to shorten the MyUI class. The MyUI::init method shrinks to a single line of code.

@Override
protected void init ( VaadinRequest vaadinRequest ) {
    this.setContent ( new ClickMeLayout () );
}

Make sure that all works. Do a clean-and-build in your IDE. Run the app to see if it works as it did originally.

Proceed to build your real app. Copy-paste that ClickMeLayout.java code, create a new class, and paste to use as a prototype. Start adding widgets to build out your new Layout. Go back to MyUI to comment-out that one line with new ClickMeLayout(), copy-paste that line to duplicate it, and alter the duplicate to call your new layout class instead of ClickMeLayout.

@Override
protected void init ( VaadinRequest vaadinRequest ) {
    // this.setContent ( new ClickMeLayout () );
    this.setContent ( new InvoiceListingLayout () );
}

I suggest keeping the ClickMeLayout around permanently in its original state. I find it to be a handy sanity-check. Eventually when your app seems to go haywire, you may not be sure if the problem is your own code or some glitch with your IDE, your web container (Tomcat, Jetty, etc.), your web browser, or who-knows-what. In that situation, you can go back to that MyUI method, disable the call to your layout, re-enable the line calling new ClickMeLayout(), do a clean-and-rebuild, and run the app. If that runs successfully, you know the fault lies within your code.


By the way, if that new lambda syntax seen in the addClickListener line throws you off, here is the equivalent code using the old Java syntax as an anonymous inner class.

this.button.addClickListener ( new Button.ClickListener () {
    @Override
    public void buttonClick ( Button.ClickEvent e ) {
        ClickMeLayout.this.addComponent ( new Label ( "Thanks" + ClickMeLayout.this.name.getValue () + ", it works!" ) );
    }
} );

Upvotes: -1

Related Questions