softshake 2013 - vaadin componentization

Post on 10-May-2015

907 Views

Category:

Self Improvement

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Vaadin extension points

Componentize your UI

7

Who am I

• Java (EE) Dev/Architect

• Vaadin enthusiast

• General blog• http://blog.frankel.ch

• @nicolas_frankel

• Vaadin specific• http://morevaadin.com

• @learnvaadin

Extension points

1. Compose base components

2. Wrap GWT widgets

3. Extensions

4. Wrap JavaScript

5. Theme

This presentation requires you have a bare minimum knowledge of Vaadin and how it works

Extension points

1. Compose base components

2. Wrap GWT widgets

3. Extensions

4. Wrap JavaScript

5. Theme

Composition

• It can’t be that hard Just compose…

• Let’s create an Address component And see for ourselves

First step

public class Address extends VerticalLayout {

public Address() {

addComponent(new TextField("Ligne 1")); addComponent(new TextField("Ligne 2")); addComponent(new TextField("Ville")); addComponent(new TextField("NPA")); }}

Input vs label

TextField ligne1 = new TextField();TextField ligne2 = new TextField();TextField ville = new TextField();TextField npa = new TextField();

ligne1.setInputPrompt("Ligne 1");ligne2.setInputPrompt("Ligne 2");ville.setInputPrompt("Ville");npa.setInputPrompt("NPA");

addComponent(ligne1);addComponent(ligne2);addComponent(ville);addComponent(npa);

Spacing

setSpacing(true);

TextField ligne1 = new TextField();TextField ligne2 = new TextField();TextField ville = new TextField();TextField npa = new TextField();

ligne1.setInputPrompt("Ligne 1");ligne2.setInputPrompt("Ligne 2");ville.setInputPrompt("Ville");npa.setInputPrompt("NPA");

addComponent(ligne1);addComponent(ligne2);addComponent(ville);addComponent(npa);

Problems?

No choice between labels and input prompts

No choice between spacing or not

i18n

Fixed layout

And so on

No configuration feature!

Configuration design

Expose your configuration features

Wrap the rest As for API

Introduce middle-components for decoupling For layout, use CustomComponent

public class Address extends CustomComponent {

private Layout layout; private TextField ligne1 = new TextField(); // Declare other components

public Address(Layout layout) {

this.layout = layout; setCompositionRoot(layout);

layout.addComponent(ligne1); // Add other components}

public void setSpacing(boolean spacing) {…} public boolean isSpacing() {…} public String getLigne1Caption() {…} // Other getters and setters}

Generic component

Configurable layout

Delegate to layout

Delegate to TextField

UI design vs data design

Address address = new Address();

address.getLigne1().setValue("M. Nicolas Frankel");address.getLigne2().setValue("Route du Simplon 1");address.getVille().setValue("Paudex");address.getNpa().setValue("1094");

Tight coupling No abstraction

No validation as a whole No JSR-303

Simple data design

An address component should display an address bean!

Better data design

Enable data buffering Commit / discard

Even better data design

(Near-)Final data design

Provide a default concrete class Help 80% of the time

Composition summary

Think about UI configuration

Think about the wrapped model

Make it easy for your developers, they are your users This makes it hard for you as the designer!

Extension points

1. Compose base components

2. Wrap GWT widgets

3. Extensions

4. Wrap JavaScript

5. Theme

Connector architectureServer-

side

"Glue" between server and client

Client-side

Connector architecture

This let us use a server-side component MyComponent

Displayed client-side MyWidget

YouTubePlayer server-side

public class YouTubePlayer extends AbstractComponent {}

YouTubeConnector client-side

@Connect(YouTubePlayer.class)public class YouTubeConnector extends AbstractComponentConnector {

public YouTubeViewer getWidget() { return (YouTubeViewer) super.getWidget(); }

protected Widget createWidget() { return new YouTubeViewer("GOKX-bGmi0k"); }}

Shared state

Communication between server and client

Shared state

Create API server-side

Create a getter/setter on the shared state

Implement onStateChanged() on the connector Call widget’s methods accordingly

YouTubeState client-side

public class YouTubeState extends AbstractComponentState {

private String movieId;

public String getMovieId() {

return movieId; }

public void setMovieId(String movieId) {

this.movieId = movieId; } }

YouTubePlayer server-side

public class YouTubePlayer extends AbstractComponent {

public YouTubePlayer(String movieId) {

getState().setMovieId(movieId); }

@Override public YouTubeState getState() {

   return (YouTubeState ) super.getState(); }}

Doesn’t necessarily mirror state nor client

YouTubeConnector client-side

@Connect(YouTubePlayer.class)public class YouTubeConnector extends AbstractComponentConnector {

// get & create widget as previously public void onStateChanged(StateChangeEvent e) {

super.onStateChanged(e); String movieId = getState().getMovieId(); getWidget().setMovieID(movieId); }}

What happens with more than one possible

state attribute?

Because it’s pays to play on the safe side

hasPropertyChanged

public void onStateChanged(StateChangeEvent e) {

super.onStateChanged(e);

if (e.hasPropertyChanged(“movieId”) {

String movieId = getState().getMovieId(); getWidget().setMovieID(movieId); } }

A constant is in order

Project structure

<root-package>

client

ClientWidget

ClientConnector

SharedState

ServerComponent

gwt.xml

Configuration

Reference widget(s) in gwt.xml

Reference gwt.xml in servlet configuration Either web.xml Or annotation

Client compilation

Only a single GWT compiled package per WAR

Compile once in the final WAR whatever the number of widgets And be done with it!

GWT widget wrap summary

1. Create server-side component API

2. Then develop what is needed Client-side widget (if necessary) Connector State

3. No need to package compiled-client code more than once

Extension points

1. Compose base components

2. Wrap GWT widgets

3. Extensions

4. Wrap JavaScript

5. Theme

Extensions

Extensions are a way to add client-side features to an existing component

Examples: Tooltip on labels Icon on text fields Caps lock warning on password fields Etc.

7

Extension architecture

Extension server-side

public class Tooltip extends AbstractExtension {

public void extend(TextField field) {

super.extend(field);

}

}

Extension client-side

@Connect(Tooltip.class)public class TooltipConnector extends AbstractExtensionConnector { @Override protected void extend(ServerConnector target) { final Widget tf = ((ComponentConnector) target).getWidget(); final VOverlay tooltip = new VOverlay(); tooltip.add(new HTML("<div class='c-tooltip'>Static tooltip</div>"));

Extension client-side continued

tf.addDomHandler(new MouseOverHandler() { @Override public void onMouseOver(MouseOverEvent event) { tooltip.showRelativeTo(tf); } }, MouseOverEvent.getType()); tf.addDomHandler(new MouseOutHandler() { @Override public void onMouseOut(MouseOutEvent event) { tooltip.hide(); } }, MouseOutEvent.getType()); }}

Usage

TextField tf = new TextField("Name");

new Tooltip().extend(tf);

And that’s all! But you need GWT skills...

Noticed the tooltip is static? "<div class='c-tooltip'>Static tooltip</div>"

Reversed order

Client-side state extension

public class TooltipState extends SharedState { private String tooltip; public String getTooltip() { return tooltip; } public void setTooltip (String tooltip) { this.tooltip = tooltip; }}

Dynamic extension server-side

public class Tooltip extends AbstractExtension { @Override public TooltipState getState() { return (TooltipState) super.getState(); } public void extend(TextField tf, String tooltip) { getState().setTooltip(tooltip); super.extend(link); }}

Dynamic Extension client-side

@Connect(TooltipExtension.class)public class TooltipConnector extends AbstractExtensionConnector { @Override public TooltipState getState() { return (TooltipState) super.getState(); }

@Override protected void extend(ServerConnector target) { … String text = getState().getTooltip(); tooltip.add(new HTML("<div class='c-tooltip'>" + text + "</div>")); … }}

Dynamic usage

TextField tf = new TextField("Name");

new Tooltip().extend(tf, "really useful tooltip");

Extension summary

1. For added client-side capacity

2. Simple usage server-side

3. Requires GWT skills

4. State to the rescue For runtime changes

Wrap JavaScript

1. Compose base components

2. Wrap GWT widgets

3. Extensions

4. Wrap JavaScript

5. Theme

7

JavaScript before 7

window.executeJavascript(String script)

“ Executes JavaScript in this window.

This method allows one to inject JavaScript from the server to client. A client implementation is not required to implement this functionality, but currently all web-based clients do implement this.

Use of this method should be avoided and instead it is recommended to create new widgets with GWT.”

JavaScript before 7

Not always feasible to wrap JavaScript in GWT

Not always available GWT skills

Not always possible to use JavaScript directly Nor maintainable when it is

JavaScript in 7

To embed scripts client-side

1. JavaScript extension To improve existing widgets

2. JavaScript component To add

JavaScript extension architecture

@JavaScript usage

On server-side JavaScript extension

Absolute resources https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js

Local resources bootstrap.js bootstrap_connector.js

Calling JS function

callFunction("functionName", arguments) Provides a way to call JavaScript scripts server-side

JavaScript extension server-side

@JavaScript({ "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js", "bootstrap.js", "bootstrap_connector.js" })public class JsTooltipExtension extends AbstractJavaScriptExtension { public void extend(TextField tf, String tooltip) { super.extend(tf); callFunction("attach", tooltip); }}

Binding with JavaScript

There’s no connector per se As there’s no GWT widget But we still need to bind betwen server and client

All local scripts have to be in the same package as the extension

JavaScript extension client-side

window.package_JavascriptTooltipExtension = function() { this.attach = function(options) { var connectorId = this.getParentId(); var element = this.getElement(connectorId); var tf = element.childNodes[0]; tf.rel = "tooltip"; tf.title = options[0]; $(tf).tooltip(); }}

Anonymous function

Namespace as underscore separated package name

Called server-

side

JavaScript and state

State as seen previously applies com.vaadin.shared.ui.JavaScriptComponentState

On client-side

window.package_JavascriptExtension = function() { this.onChange = function() { this.getState() … }}

JavaScript component

Very alike to extension But will create the HTML DIV Hopefully… never played with it

JavaScript component architecture

Wrap JavaScript summary

When JavaScript is available Just need a JS connector script

Pros No need to use GWT Can still be packaged as JARs

Cons Loses all static typing

Themes

1. Compose base components

2. Wrap GWT widgets

3. Extensions

4. Wrap JavaScript

5. Theme

7

Themes

Themes are an easy way to change applications appearance CSS / SASS Images HTML layouts

@Theme

Provided OOTB reindeer (default) runo chameleon

7

Theme structure

VAADIN/themes

mytheme.scss

styles.scss

img

layouts

Themes are undervalued

SASS is extra-powerful

Very easy to use

Packaged in JARs

Front-end and Java developers can work in parallel

Summary

Vaadin is Component-Oriented Create your own Design for reusability

Use it to your advantage!

Thanks for your attention ?

top related