Develop New Components

Reference

(Version 7) https://vaadin.com/docs/-/part/framework/clientside/clientside-widget.html

https://vaadin.com/docs/-/part/framework/clientside/clientside-module.html#clientside.module

https://vaadin.com/docs/-/part/framework/gwt/gwt-overview.html#gwt.overview (everything is here)

Project Setup

Eclipse "File->New->Other->Vaadin 7 Project (Maven) -> Add-on Project (vaadin-archetype-widget)"

3 Maven projects are created:

  • myaddon-root
    • contains a root pom.xml and two children projects
  • myaddon
    • the custom component
  • myaddon-demo
    • the demo application to utilize the client side component
    • test "mvn jetty:run"

File / Class structure

Custom Shared State

Example shared state.

package com.example.myaddon.client;
public class MyComponentState extends com.vaadin.shared.AbstractComponentState {
    // State can have both public variable and bean properties
    public String text = "MyComponent";
}

Custom shared state usually extends com.vaadin.shared.AbstractComponentState, which:

  • further extends com.vaadin.shared.communication.SharedState;
  • defines certain common component states including:https://sites.google.com/site/bingsite/web-development/vaadin/book-of-vaadin-notes/develop-new-components#TOC-Custom-ClientPRC
    • String height
    • String width
    • boolean readOnly
    • boolean immediate
    • String description
    • String caption
    • List<String> styles
    • String id
    • String primaryStyleNamre
    • String errorMessage
    • boolean captionAsHtml
  • SharedState, root of shared state, implements Serializable and defines automatically managed resources used by the connector:
    • public Map<String, URLReference> resources = new HashMap<String, URLReference>();
    • public boolean enabled = true;
    • public Set<String> registeredEventListeners = null;

Read the document of the root SharedState class:

/**

* Interface to be implemented by all shared state classes used to communicate

* basic information about a {@link Connector} from server to client.

*

* Shared state classes have to be declared in shared package to be accessible

* both for server and client code.

*

* Shared state objects are only sent from the server to the client, and any

* modifications from the client should be performed via an RPC call that

* modifies the authoritative state on the server.

*

* A shared state class should be a bean with getters and setters for each

* field. Supported data types are simple Java types, other beans and maps and

* arrays of these.

*

* On the client side the connector should override

* {@link com.vaadin.client.ui.AbstractConnector#getState()} to return the

* correct state type. This automatically causes a correct state object to be

* created.

*

* Subclasses of a {@link Connector} using shared state should also provide a

* subclass of the shared state class of the parent class to extend the state. A

* single {@link Connector} can only have one shared state object.

*

* @since 7.0

*/

Client Side

Start with existing GWT widget or design new one. Usually GWT widget should be enough. Very rare needs to design new one with Javascript.

Custom GWT widget refer to here: http://www.gwtproject.org/doc/latest/DevGuideUiCustomWidgets.html

Read this for style GWT widget http://www.gwtproject.org/javadoc/latest/com/google/gwt/user/client/ui/UIObject.html

Client side source code must be under "client" package under the package of descriptor file

Client-side Module Descriptor

Client-side modules are defined in a module descriptor gwt.xml file. Normally need to inherit DefaultWidgetSet (created from archetype).

  • Stylesheet is included here <stylesheet src="mywidget/styles.css"/> (path is relative to the public folder under the folder of descriptor
  • Limiting compile targets during development : (list see here)
      • <set-property name="user.agent" value="gecko1_8"/>

Custom ClientPRC

package com.example.myaddon.client;
import com.vaadin.shared.communication.ClientRpc;
// ClientRpc is used to pass events from server to client
// For sending information about the changes to component state, use State instead
public interface MyComponentClientRpc extends ClientRpc {
    // Example API: Fire up alert box in client
    public void addStyleName(int index, String styleName);
   
    public void removeStyleName(int index, String styleName);
}

ClientRPC is interface that server uses to call client. ClientRpc interface is just a marker and extends Serializable.

On server side

com.vaadin.ui.AbstractComponent (which extends AbstractClientConnector) can obtain a proxy instance of the interface by calling

   protected <T extends ClientRpc> T getRpcProxy(final Class<T> rpcInterface)

This will obtain the right proxy.

On client side

The AbstractComponentConnector needs to obtain an instance of the custom ClientRpc (implementation of, that is), and register it with method below of com.vaadin.client.ui.AbstractComponentConnector (which inherits it from AbstractConnector)

protected <T extends ClientRpc> void registerRpc(Class<T> rpcInterface,
            T implementation)

Custom ServerRpc

ServerRpc is also just a marker interface and extends Serializable. It's interface that client call server.

package com.example.myaddon.client;
...
// ServerRpc is used to pass events from client to server
public interface MyComponentServerRpc extends ServerRpc {
    public void msgServer(String message);
}

On client side

Uses com.vaadin.client.communication.RpcProxy's method below to obtain an instance.

public static <T extends ServerRpc> T create(Class<T> rpcInterface,
            ServerConnector connector)

It's best to just call from within the body of an AbstractComponentConnector's definition:

MyComponentServerRpc rpc = RpcProxy.create(MyComponentServerRpc.class, this);

On Server Side

Server side needs to provide an implementation and register it.

    // To process events from the client, we implement ServerRpc
    private MyComponentServerRpc rpc = new MyComponentServerRpc() {
        @Override
        public void msgServer(String message) {
            System.err.println("Clicked: "+message);
        }
    };
    ......
    registerRpc(rpc);

Register with AbstractComponent's method below (inherited from AbstractClientConnector):

protected <T extends ServerRpc> void registerRpc(T implementation) {

Custom AbstractComponentConnector

Connector binds client side widget class to server side component class. It runs in client and uses @Connect annotation to specifies corresponding server-side component

package com.example.myaddon.client;

....

@Connect(MyComponent.class)
public class MyComponentConnector extends AbstractComponentConnector {
    // ServerRpc is used to send events to server. Communication implementation
    // is automatically created here
    MyComponentServerRpc rpc = RpcProxy.create(MyComponentServerRpc.class, this);
    public MyComponentConnector() {
       
        // To receive RPC events from server, we register ClientRpc implementation
        registerRpc(MyComponentClientRpc.class, new MyComponentClientRpc() {
            @Override
            public void addStyleName(int index, String styleName) {
                ...
            }
            ... // implementations //.....
        });
    }
    // We must implement getWidget() to cast to correct type
    // (this will automatically create the correct widget type)
    @Override
    public MyComponentWidget getWidget() {
        return (MyComponentWidget) super.getWidget();
    }
    // We must implement getState() to cast to correct type
    @Override
    public MyComponentState getState() {
        return (MyComponentState) super.getState();
    }
    // Whenever the state changes in the server-side, this method is called
    @Override
    public void onStateChanged(StateChangeEvent stateChangeEvent) {
        super.onStateChanged(stateChangeEvent);
        // State is directly readable in the client after it is set in server
        getWidget().updateSharedState(getState());
    }
}

Custom Widget Set

Widget class can extend any GWT Widget. Good practice to specify a distinct style. GWT Composite is usually a good choice, see GWT custom widget.

.....
public class MyComponentWidget extends ScrollPanel {
    ...
    public MyComponentWidget() {
        // CSS class-name should not be v- prefixed
        setStyleName("myaddon");
       
        ... initialize ui ...
    }
   

......

   
 
}

Defines a GWT widge set, in ./myaddon-addon/src/main/resources/com/example/myaddon/WidgetSet.gwt.xml (generated from archetype and generally not needed to modify)

<module>
    <!-- WS Compiler: manually edited -->
    <!-- Inherit DefaultWidgetSet -->
    <inherits name="com.vaadin.DefaultWidgetSet" />
    <!-- Widget styles in public -directory -->
    <stylesheet src="myaddon/styles.css"/>
</module>

Server Side

package com.example.myaddon;

...

// This is the server-side UI component that provides public API
// for MyComponent
public class MyComponent extends com.vaadin.ui.AbstractComponent {
......
    // To process events from the client, we implement ServerRpc
    // Bing: this is how server component response to event sent from client
    // Bing: implement a custom server PRC and register
    private MyComponentServerRpc rpc = new MyComponentServerRpc() {
        // Event received from client - user clicked our widget
        public void clicked(MouseEventDetails mouseDetails) {
           
            // Bing: look like here sends back response to client through client PRC
            // Send nag message every 5:th click with ClientRpc
            if (++clickCount % 5 == 0) {
                getRpcProxy(MyComponentClientRpc.class)
                        .alert("Ok, that's enough!");
            }
           
            // Update shared state. This state update is automatically
            // sent to the client.
            getState().text = "You have clicked " + clickCount + " times";
        }
    };
    public MyComponent() {
        // To receive events from the client, we register ServerRpc
        registerRpc(rpc);
    }
    // We must override getState() to cast the state to MyComponentState
    @Override
    protected MyComponentState getState() {
        return (MyComponentState) super.getState();
    }
}

Component and UI Extensions

This method add features to existing components without writing it entirely and without writing the client-side widget. See https://vaadin.com/docs/-/part/framework/gwt/gwt-extension.html

An example is to extend PasswordField widget to give warning with caps locked.

Styling

References

https://vaadin.com/docs/-/part/framework/gwt/gwt-styling.html

https://vaadin.com/wiki/-/wiki/Main/Widget+styling+using+only+CSS

Basics

Default style should be defined in the widget sources

Different themes can then modify the style

Styling in widget classes

Style the composite widget with overall style and with separate styles for sub-widgets as well. Use setStyleName().

public class MyPickerWidget extends ComplexPanel {     public static final String CLASSNAME = "mypicker";     ......     public MyPickerWidget() { 
        setElement(Document.get().createDivElement());         setStylePrimaryName(CLASSNAME);          textBox.setStylePrimaryName(CLASSNAME + "-field");         button.setStylePrimaryName(CLASSNAME + "-button");          add(textBox, getElement());         add(button, getElement());         ......     } }

In addition, all Vaadin components get the v-widget class. If it extends an existing Vaadin or GWT widget, it inhgerits CSS classes as well. v-widget sets box-sizing to border-box, causes borders and paddlings to be considered.

Default stylesheet

Stylesheets must be placed under "public" folder under folder of widget set. Example:

.mypicker {  white-space: nowrap; }  .mypicker-button {  display: inline-block;  border: 1px solid black;  padding: 3px;  width: 15px;  text-align: center; }

Vaadin 7 adds a v-has-width/v-has-height class to the component's main element when width/height of the widget is set (other than undefined). Addition CSS can make use of this to make further adjustment.

.mypicker.v-has-width > .mypicker-field {
    width: 100%;
}

(CSS: E>F means an F elememnt child of an E element)

My observation

To style my widget like a Vaadin widget, style with an "official" style. For example, style a text area with "v-textarea" give it the style just like the official textarea: when on focus a decoration border is shown.

Use browser developer kit to observe generated DOM.

Use "?debug" debug mode. Sometimes local widget throws an uncatched exception, this will show only in debug mode.

Official style sheet can be found in "Maven Dependencies -> vaadin-themes-x.x.x.jar"