--- | |||||
title: Access Control For Views | |||||
order: 46 | |||||
layout: page | |||||
--- | |||||
[[access-control-for-views]] | |||||
Access control for views | |||||
------------------------ | |||||
The Navigator API provides a simple mechanism to allow or disallow | |||||
navigating to a View. Before a View is shown, each ViewChangeListener | |||||
that is registered with the Navigator is given the opportunity to veto | |||||
the View change. | |||||
One can also make the View itself trigger a navigation to another View | |||||
in navigateTo(), but let's take a look at the more flexible | |||||
beforeViewChange() and afterViewChange(), that exists specifically for | |||||
this purpose. | |||||
First, let's continue from previous examples and create a MessageView | |||||
for secret messages: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.View; | |||||
import com.vaadin.ui.Label; | |||||
public class SecretView extends MessageView implements View { | |||||
public static final String NAME = "secret"; | |||||
public SecretView() { | |||||
setCaption("Private messages"); | |||||
((Layout) getContent()).addComponent(new Label("Some private stuff.")); | |||||
} | |||||
} | |||||
.... | |||||
As you can see, there is absolutely nothing special going on here, we | |||||
just customize the View enough to be able to distinguish from the | |||||
regular MessageView. | |||||
Next, we'll register this new View with the Navigator, exactly as | |||||
before. At this point our SecretView is not secret at all, but let's fix | |||||
that by adding a ViewChangeListener to the Navigator: | |||||
[source,java] | |||||
.... | |||||
navigator.addViewChangeListener(new ViewChangeListener() { | |||||
@Override | |||||
public boolean beforeViewChange(ViewChangeEvent event) { | |||||
if (event.getNewView() instanceof SecretView && | |||||
((NavigationtestUI)UI.getCurrent()).getLoggedInUser() == null) { | |||||
Notification.show("Permission denied", Type.ERROR_MESSAGE); | |||||
return false; | |||||
} else { | |||||
return true; | |||||
} | |||||
} | |||||
@Override | |||||
public void afterViewChange(ViewChangeEvent event) { | |||||
} | |||||
}); | |||||
.... | |||||
So if we're on our way to the SecretView, but not logged in | |||||
(getLoggedInUser() == null), the View change is cancelled. Quite simple | |||||
rules in our case, but you could check anything - most probably you'll | |||||
want to call a helper method that checks the user for permission. | |||||
Let's go ahead and add some links to the MainView again, so that we | |||||
don't have to muck with the address-bar to try it out: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.View; | |||||
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; | |||||
import com.vaadin.server.ExternalResource; | |||||
import com.vaadin.ui.Button; | |||||
import com.vaadin.ui.Button.ClickEvent; | |||||
import com.vaadin.ui.Link; | |||||
import com.vaadin.ui.Panel; | |||||
import com.vaadin.ui.UI; | |||||
import com.vaadin.ui.VerticalLayout; | |||||
public class MainView extends Panel implements View { | |||||
public static final String NAME = ""; | |||||
public MainView() { | |||||
VerticalLayout layout = new VerticalLayout(); | |||||
Link lnk = new Link("Count", new ExternalResource("#!" + CountView.NAME)); | |||||
layout.addComponent(lnk); | |||||
lnk = new Link("Message: Hello", new ExternalResource("#!" | |||||
+ MessageView.NAME + "/Hello")); | |||||
layout.addComponent(lnk); | |||||
lnk = new Link("Message: Bye", new ExternalResource("#!" | |||||
+ MessageView.NAME + "/Bye/Goodbye")); | |||||
layout.addComponent(lnk); | |||||
lnk = new Link("Private message: Secret", new ExternalResource("#!" | |||||
+ SecretView.NAME + "/Secret")); | |||||
layout.addComponent(lnk); | |||||
lnk = new Link("Private message: Topsecret", new ExternalResource("#!" | |||||
+ SecretView.NAME + "/Topsecret")); | |||||
layout.addComponent(lnk); | |||||
// login/logout toggle so we can test this | |||||
Button logInOut = new Button("Toggle login", | |||||
new Button.ClickListener() { | |||||
public void buttonClick(ClickEvent event) { | |||||
Object user = ((NavigationtestUI)UI.getCurrent()).getLoggedInUser(); | |||||
((NavigationtestUI)UI.getCurrent()).setLoggedInUser( | |||||
user == null ? "Smee" : null); | |||||
} | |||||
}); | |||||
layout.addComponent(logInOut); | |||||
setContent(layout); | |||||
} | |||||
@Override | |||||
public void enter(ViewChangeEvent event) { | |||||
} | |||||
} | |||||
.... | |||||
Instead of just showing a notification and leaving the user wondering, | |||||
we should obviously allow the user to log in and continue. We'll do just | |||||
that in the separate tutorial about Handling login, but for now we just | |||||
add a button that toggles our logged in/out state. | |||||
Meanwhile, here is the the full source for the UI so far: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.Navigator; | |||||
import com.vaadin.navigator.ViewChangeListener; | |||||
import com.vaadin.server.VaadinRequest; | |||||
import com.vaadin.ui.Notification; | |||||
import com.vaadin.ui.Notification.Type; | |||||
import com.vaadin.ui.UI; | |||||
public class NavigationtestUI extends UI { | |||||
Navigator navigator; | |||||
String loggedInUser; | |||||
@Override | |||||
public void init(VaadinRequest request) { | |||||
// Create Navigator, make it control the ViewDisplay | |||||
navigator = new Navigator(this, this); | |||||
// Add some Views | |||||
navigator.addView(MainView.NAME, new MainView()); // no fragment | |||||
// #!count will be a new instance each time we navigate to it, counts: | |||||
navigator.addView(CountView.NAME, CountView.class); | |||||
// #!message adds a label with whatever it receives as a parameter | |||||
navigator.addView(MessageView.NAME, new MessageView()); | |||||
// #!secret works as #!message, but you need to be logged in | |||||
navigator.addView(SecretView.NAME, new SecretView()); | |||||
// we'll handle permissions with a listener here, you could also do | |||||
// that in the View itself. | |||||
navigator.addViewChangeListener(new ViewChangeListener() { | |||||
@Override | |||||
public boolean beforeViewChange(ViewChangeEvent event) { | |||||
if (event.getNewView() instanceof SecretView | |||||
&& ((NavigationtestUI)UI.getCurrent()).getLoggedInUser() == null) { | |||||
Notification.show("Permission denied", Type.ERROR_MESSAGE); | |||||
return false; | |||||
} else { | |||||
return true; | |||||
} | |||||
} | |||||
@Override | |||||
public void afterViewChange(ViewChangeEvent event) { | |||||
System.out.println("After view change"); | |||||
} | |||||
}); | |||||
} | |||||
public String getLoggedInUser(){ | |||||
return loggedInUser; | |||||
} | |||||
public void setLoggedInUser(String user){ | |||||
loggedInUser = user; | |||||
} | |||||
} | |||||
.... |
To try out how the widths of Grid columns work in different situations, | To try out how the widths of Grid columns work in different situations, | ||||
we'll use the same base implementation as in the | we'll use the same base implementation as in the | ||||
link:UsingGridWithAContainer.asciidoc[Using Grid with a Container] | |||||
example. | |||||
<<UsingGridWithAContainer#using-grid-with-a-container, | |||||
Using Grid with a Container>> example. | |||||
Grid does by default check the widths of all cells on the first pageful | Grid does by default check the widths of all cells on the first pageful | ||||
of data and allocate column widths based on that. If there's room to | of data and allocate column widths based on that. If there's room to |
--- | |||||
title: Creating A Bookmarkable Application With Back Button Support | |||||
order: 55 | |||||
layout: page | |||||
--- | |||||
[[creating-a-bookmarkable-application-with-back-button-support]] | |||||
Creating a bookmarkable application with back button support | |||||
------------------------------------------------------------ | |||||
Vaadin 7 comes with a new set of APIs to aid creation of navigation | |||||
within your application. The main concepts are *Navigator* and *View*, | |||||
and using these you can easily create an application that supports the | |||||
standard browser methods for navigation; bookmarking, history, back- and | |||||
forward navigation using browser buttons. This is (usually) done using | |||||
browser "fragments" (the stuff after the #-character in the URI). | |||||
At the same time, the API provides a natural way of partitioning your | |||||
application into views - something most applications did previously | |||||
anyway, but previously without framework 'guidance'. | |||||
Let's start by making a View that counts the times it has been created. | |||||
This is a simple example, but will later shed some light on when Views | |||||
are created, but let's not worry about that just yet: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.View; | |||||
import com.vaadin.ui.Label; | |||||
import com.vaadin.ui.Panel; | |||||
public class CountView extends Panel implements View { | |||||
public static final String NAME = "count"; | |||||
private static int count = 1; | |||||
public CountView() { | |||||
setContent(new Label("Created: " + count++)); | |||||
} | |||||
public void enter(ViewChangeEvent event) { | |||||
} | |||||
} | |||||
.... | |||||
We'll extend Panel as a convenient base, and add a Label to that in the | |||||
constructor, updating the static count. The _enter()_ -method comes from | |||||
View, and is called when our View is activated, but we'll do nothing | |||||
about that in our simplistic View. | |||||
Note the _static final NAME_: we'll use it instead of a 'magic' string | |||||
when we register the View with the Navigator later. Feel free to use any | |||||
method you like to keep track of your View-names (e.g Enum, simpleName | |||||
of the View's class, and so on…) | |||||
In order to do any navigating, we'll need at least two views, so let's | |||||
create a main view that has a link to the counting view we just created. | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.View; | |||||
import com.vaadin.server.ExternalResource; | |||||
import com.vaadin.ui.Link; | |||||
import com.vaadin.ui.Panel; | |||||
public class MainView extends Panel implements View { | |||||
public static final String NAME = ""; | |||||
public MainView() { | |||||
Link lnk = new Link("Count", new ExternalResource("#!" | |||||
+ CountView.NAME)); | |||||
setContent(lnk); | |||||
} | |||||
public void enter(ViewChangeEvent event) { | |||||
} | |||||
} | |||||
.... | |||||
Note the empty string used as _NAME_. This is because we want this to be | |||||
our main ("home") View, displayed before any navigation is done. | |||||
In this example we use a Link and let the browser do the navigating. We | |||||
could just as easily use a Button and tell the Navigator where we want | |||||
to go when the button's ClickListener is invoked. Note that we're using | |||||
_CountView.NAME_, and what we're actually doing is using the "fragment" | |||||
part of the application URI to indicate the view. The resulting URI will | |||||
look something like http://.../application#!count . | |||||
Ok, one last thing: we need to set up a UI with a Navigator, and | |||||
register our views: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.Navigator; | |||||
import com.vaadin.navigator.Navigator.SimpleViewDisplay; | |||||
import com.vaadin.server.Page; | |||||
import com.vaadin.server.WrappedRequest; | |||||
import com.vaadin.ui.UI; | |||||
public class NavigationtestUI extends UI { | |||||
@Override | |||||
public void init(VaadinRequest request) { | |||||
// Create Navigator, use the UI content layout to display the views | |||||
Navigator navigator = new Navigator(this, this); | |||||
// Add some Views | |||||
navigator.addView(MainView.NAME, new MainView()); // no fragment | |||||
// #!count will be a new instance each time we navigate to it, counts: | |||||
navigator.addView(CountView.NAME, CountView.class); | |||||
// The Navigator attached to the UI will automatically navigate to the initial fragment once | |||||
// the UI has been initialized. | |||||
} | |||||
} | |||||
.... | |||||
There are advanced ways to use the Navigator API, and there are simple | |||||
ways. Most applications will do fine with the simple ways, and the | |||||
Navigator constructor we used is written that in mind. It simply takes | |||||
any ComponentContainer, assumes that all our Views are also Components, | |||||
and on a view change sets the given view as the ComponentContainer's | |||||
only child. Internally, it uses a _ViewDisplay_ subclass called | |||||
ComponentContainerViewDisplay to do this. If we had more advanced | |||||
requirements, we could write our own ViewDisplay subclass to show our | |||||
views in whatever fashion we'd like. | |||||
The Navigator finds out about URI fragment changes through the Page, and | |||||
directs the ViewDisplay accordingly. We register our Views using | |||||
_addView()_ so that the Navigator knows how to connect fragments with | |||||
Views. Again notice how we use the static NAME instead of | |||||
_addView("name", view)_ - but feel free to use other approaches. | |||||
In order to illustrate how the two differ, we register an _instance_ of | |||||
the MainView, but _CountView.class_. As a result, the MainView is | |||||
created once, when the UI is created, and lives as long as the UI lives. | |||||
On the other hand, a new CountView instance will be created each time we | |||||
navigate to it (but no earlier). You can try navigating back-and-forth | |||||
and see how the count is updated - try registering it using new | |||||
CountView() instead… | |||||
It's also good to keep in mind that a new UI is created each time you | |||||
press reload in the browser, unless you use the @PreserveOnRefresh | |||||
annotation on the UI. |
--- | |||||
title: Creating A Component Extension | |||||
order: 51 | |||||
layout: page | |||||
--- | |||||
[[creating-a-component-extension]] | |||||
Creating a component extension | |||||
------------------------------ | |||||
In this tutorial we create a simple extension that can be attached to a | |||||
`PasswordField`, displaying a floating notification if the user's Caps | |||||
Lock seems to be enabled. We assume the reader is already familiar with | |||||
the <<CreatingAUIExtension#creating-a-ui-extension,Creating a UI extension>> | |||||
tutorial. | |||||
This extension has almost no server-side functionality; the whole Extension | |||||
class is as follows: | |||||
[source,java] | |||||
.... | |||||
public class CapsLockWarning extends AbstractExtension { | |||||
protected CapsLockWarning(PasswordField field) { | |||||
// Non-public constructor to discourage direct instantiation | |||||
extend(field); | |||||
} | |||||
public static CapsLockWarning warnFor(PasswordField field) { | |||||
return new CapsLockWarning(field); | |||||
} | |||||
} | |||||
.... | |||||
When there's nothing to configure for the extension, users just want to | |||||
enable it for some component and be done with it. By defining a static | |||||
factory method, the user only needs to do something like | |||||
`CapsLockWarning.warnFor(myPasswordField);` to make `myPasswordField` | |||||
get the new functionality. | |||||
The client side is not overly complicated, either. We override the | |||||
`extend` method, called by the framework when the client-side extension | |||||
connector is attached to its target the client-side counterpart of the | |||||
connector to which the server-side extension instance is attached in | |||||
this case, `PasswordFieldConnector`. | |||||
We add a key press handler to the password widget, checking if the input | |||||
looks like Caps Lock might be enabled. The Caps Lock state cannot be | |||||
directly queried in GWT/JavaScript, so we use a trick: check if either | |||||
* the shift key was not held but the entered character was uppercase, or | |||||
* the shift key _was_ held but the entered character was lowercase. | |||||
If this is the case, we show a warning in the form of a floating widget | |||||
(`VOverlay`). This demonstrates how an extension may make use of UI | |||||
elements even though it is not a part of the layout hierarchy. A | |||||
frequent use case for extensions is showing different types of floating | |||||
overlay elements that are temporary in character. | |||||
[source,java] | |||||
.... | |||||
@Connect(CapsLockWarning.class) | |||||
public class CapsLockWarningConnector extends AbstractExtensionConnector { | |||||
@Override | |||||
protected void extend(ServerConnector target) { | |||||
final Widget passwordWidget = ((ComponentConnector) target).getWidget(); | |||||
final VOverlay warning = new VOverlay(); | |||||
warning.setOwner(passwordWidget); | |||||
warning.add(new HTML("Caps Lock is enabled!")); | |||||
passwordWidget.addDomHandler(new KeyPressHandler() { | |||||
@Override | |||||
public void onKeyPress(KeyPressEvent event) { | |||||
if (isEnabled() && isCapsLockOn(event)) { | |||||
warning.showRelativeTo(passwordWidget); | |||||
} else { | |||||
warning.hide(); | |||||
} | |||||
} | |||||
}, KeyPressEvent.getType()); | |||||
} | |||||
private boolean isCapsLockOn(KeyPressEvent e) { | |||||
return e.isShiftKeyDown() ^ Character.isUpperCase(e.getCharCode()); | |||||
} | |||||
} | |||||
.... | |||||
To use the Caps Lock warning, compile your widgetset and extend a | |||||
PasswordField with something like this | |||||
[source,java] | |||||
.... | |||||
PasswordField field = new PasswordField("Enter your password"); | |||||
CapsLockWarning.warnFor(field); | |||||
addComponent(field); | |||||
.... |
--- | |||||
title: Creating A Simple Component | |||||
order: 48 | |||||
layout: page | |||||
--- | |||||
[[creating-a-simple-component]] | |||||
Creating a simple component | |||||
--------------------------- | |||||
To make a component with a new client-side widget (as opposed to making | |||||
a server-side composite), you will need to make three things: the | |||||
_server-side component_ you'll actually use in your application (let's | |||||
call it *MyComponent*), the corresponding _client-side (GWT) widget_ | |||||
that will render your component in the browser (*MyComponentWidget*) and | |||||
a _Connector_ that handles the communication between the two | |||||
(*MyComponentConnector*). (Note that although MyComponentWidget could in | |||||
principle be a Connector as well, in practice it's a good idea to | |||||
separate the two.) | |||||
At this point the basic MyComponent has no functionality except | |||||
inherited basic component features (we'll add functionality in following | |||||
articles): | |||||
[source,java] | |||||
.... | |||||
package com.example.mycomponent; | |||||
import com.vaadin.ui.AbstractComponent; | |||||
public class MyComponent extends AbstractComponent { | |||||
} | |||||
.... | |||||
The main thing to notice here is that it inherits `AbstractComponent`, | |||||
which is the most common case (unless it will contain other components, | |||||
see separate article about component containers). The component will | |||||
automatically have the basic component features, such as size and | |||||
caption. | |||||
At this point our basic client-side widget will just statically render | |||||
some text: | |||||
[source,java] | |||||
.... | |||||
package com.example.mycomponent.client; | |||||
import com.google.gwt.user.client.ui.Label; | |||||
public class MyComponentWidget extends Label { | |||||
public static final String CLASSNAME = "mycomponent"; | |||||
public MyComponentWidget() { | |||||
setText("This is MyComponent"); | |||||
setStyleName(CLASSNAME); | |||||
} | |||||
} | |||||
.... | |||||
Notice that this is actually a plain GWT widget that can be used as any | |||||
other GWT widget. It's a good idea to set a style name from the start, | |||||
so that the component can be styled. | |||||
Now all we have to do is connect the component to the widget using a | |||||
Connector: | |||||
[source,java] | |||||
.... | |||||
package com.example.mycomponent.client; | |||||
import com.example.mycomponent.MyComponent; | |||||
import com.google.gwt.core.client.GWT; | |||||
import com.google.gwt.user.client.ui.Widget; | |||||
import com.vaadin.client.ui.AbstractComponentConnector; | |||||
import com.vaadin.client.ui.Connect; | |||||
@Connect(com.example.mycomponent.MyComponent.class) | |||||
public class MyComponentConnector extends AbstractComponentConnector { | |||||
@Override | |||||
protected Widget createWidget() { | |||||
return GWT.create(MyComponentWidget.class); | |||||
} | |||||
} | |||||
.... | |||||
The *crucial Connect annotation* is what actually tells the framework | |||||
what is connected where - do this first, since it's easy to forget. | |||||
In `createWidget()` use `GWT.create()` instead of `new` whenever possible, | |||||
since it allows for some flexibility that might come in handy later on. | |||||
Though this is optional, you might also want to override getWidget() so | |||||
that you can narrow it's return type from Widget to your actual | |||||
implementation class: | |||||
[source,java] | |||||
.... | |||||
@Override | |||||
public MyComponentWidget getWidget() { | |||||
return (MyComponentWidget) super.getWidget(); | |||||
} | |||||
.... | |||||
The package structure usually looks something like this: | |||||
* com.example.mycomponent | |||||
** MyComponent.java | |||||
** MyComponentWidgetset.gwt.xml | |||||
* com.example.mycomponent.client | |||||
** MyComponentConnector.java | |||||
** MyComponentWidget.java | |||||
Finally, compile the widgetset, and *make sure the widgetset is defined with the @Widgetset annotation in the UI class*: | |||||
[source,java] | |||||
.... | |||||
@Widgetset("com.example.mycomponent.MyComponentWidgetset") | |||||
class MyUI extends UI { | |||||
.... | |||||
If you are using web.xml, it should contain the widgetset parameter: | |||||
[source,xml] | |||||
.... | |||||
<servlet> | |||||
<servlet-name>My Vaadin App</servlet-name> | |||||
<servlet-class>com.vaadin.server.VaadinServlet</servlet-class> | |||||
<init-param> | |||||
<description>Vaadin UI</description> | |||||
<param-name>UI</param-name> | |||||
<param-value>com.example.myexampleproject.MyApplicationUI</param-value> | |||||
</init-param> | |||||
<init-param> | |||||
<param-name>widgetset</param-name> | |||||
<param-value>com.example.mycomponent.MyComponentWidgetset</param-value> | |||||
</init-param> | |||||
</servlet> | |||||
.... | |||||
Add MyComponent to your application, and it should render a label saying | |||||
"This is MyComponent". | |||||
Next have a look at the articles covering shared state and RPC, to learn | |||||
how to add more functionality to the component. |
--- | |||||
title: Creating A Simple Component Container | |||||
order: 49 | |||||
layout: page | |||||
--- | |||||
[[creating-a-simple-component-container]] | |||||
Creating a simple component container | |||||
------------------------------------- | |||||
Components in Vaadin can be roughly split into two groups, `Component`{empty}s | |||||
and `ComponentContainer`{empty}s. ComponentContainers are Components in | |||||
themselves which can also contain other components. If you are about to | |||||
implement a component that contains other components, then you'll get a | |||||
headstart by extending Vaadin's `ComponentContainer`. The biggest feature | |||||
is in tranferring the list of server side components from your component | |||||
to the client. Here's how you do it. | |||||
[[server-side]] | |||||
Server Side | |||||
^^^^^^^^^^^ | |||||
To start of we implement our server side component. For this we extend | |||||
the ready made abstract implementation `AbstractComponentContainer`. This | |||||
requires us to implement `addComponent(Component)`, | |||||
`removeComponent(Component)`, `replaceComponent(Component, Component)`, | |||||
`getComponentCount` and `getComponentIterator()`. | |||||
[source,java] | |||||
.... | |||||
package com.example.widgetcontainer; | |||||
import java.util.ArrayList; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import com.vaadin.ui.AbstractComponentContainer; | |||||
import com.vaadin.ui.Component; | |||||
public class WidgetContainer extends AbstractComponentContainer { | |||||
List<Component> children = new ArrayList<Component>(); | |||||
@Override | |||||
public void addComponent(Component c) { | |||||
children.add(c); | |||||
super.addComponent(c); | |||||
markAsDirty(); | |||||
} | |||||
@Override | |||||
public void removeComponent(Component c) { | |||||
children.remove(c); | |||||
super.removeComponent(c); | |||||
markAsDirty(); | |||||
} | |||||
public void replaceComponent(Component oldComponent, Component newComponent) { | |||||
int index = children.indexOf(oldComponent); | |||||
if (index != -1) { | |||||
children.remove(index); | |||||
children.add(index, newComponent); | |||||
fireComponentDetachEvent(oldComponent); | |||||
fireComponentAttachEvent(newComponent); | |||||
markAsDirty(); | |||||
} | |||||
} | |||||
public int getComponentCount() { | |||||
return children.size(); | |||||
} | |||||
public Iterator<Component> iterator() { | |||||
return children.iterator(); | |||||
} | |||||
} | |||||
.... | |||||
Add, remove and replace are quite straightforward. In the class we | |||||
upkeep a list of children internally, and these three methods modify | |||||
them. Add and remove have ready made methods in the super class for | |||||
notifying all event handlers that the children have changed and because | |||||
of that we should make calls to the super methods after we have updated | |||||
the list. In `replaceComponent` we have to call | |||||
`fireComponentDetachEvent(Component)` and | |||||
`fireComponentAttachEvent(Component)` to manually trigger these events. In | |||||
all three methods we should also call `markAsDirty` as a last step to | |||||
notify the client side that the children have changed. | |||||
The methods `getComponentCount()` and `iterator()` takes care of providing | |||||
the required information that we need to the client side. Here they are | |||||
simple delegate methods to the List's `size()` and `iterator()`. | |||||
[[client-side]] | |||||
Client Side | |||||
^^^^^^^^^^^ | |||||
Next up, we want to set up a standard GWT widget which will be our | |||||
component container's client side widget. GWT in itself has a bunch of | |||||
component containers in it. In GWT, these are called Panels. For this | |||||
case I will start with a `VerticalPanel`. It is roughly the same as | |||||
`VerticalLayout` in Vaadin. Down the road you want to edit this file to | |||||
add features or even extend Widget to create a complete custom widget. | |||||
For now extending `VerticalPanel` is enough and we'll use that as-is. | |||||
[source,java] | |||||
.... | |||||
package com.example.widgetcontainer.client.ui; | |||||
import com.google.gwt.user.client.ui.VerticalPanel; | |||||
public class VWidgetContainer extends VerticalPanel { | |||||
public static final String CLASSNAME = "v-widgetcontainer"; | |||||
public VWidgetContainer() { | |||||
setStyleName(CLASSNAME); | |||||
} | |||||
} | |||||
.... | |||||
[[connector]] | |||||
Connector | |||||
^^^^^^^^^ | |||||
Your widget's Connector will transfer the components from the server | |||||
side as child widgets to our widget. The connector will feed the | |||||
children to the panel trough it's standard API, namely `add(Widget)`, | |||||
`remove(Widget)` and `clear();` | |||||
Instead of going the standard route of extending | |||||
`AbstractComponentConnector` as your connector, here we can take use of | |||||
Vaadin's internal features and extend | |||||
`AbstractComponentContainerConnector`. Additionally to implementing the | |||||
`getWidget()` -method from `AbstractComponentConnector`, we also have to | |||||
supply the class with an implementation to a method called | |||||
`updateCaption(ComponentConnector)`. This method is there if we want the | |||||
container to take care of the captions for all the components. We don't | |||||
need to take care of these captions in this example so we can leave the | |||||
implementation empty. | |||||
The real benefit of extending `AbstractComponentContainerConnector` is | |||||
that we can now extend a method called | |||||
`onConnectorHierarchyChange(ConnectorHierarchyChangeEvent)`. This method | |||||
will be called every time that the server side calls `markAsDirty()` if | |||||
the component hierarchy has been changed. From within it, we can call on | |||||
`getChildComponents` to get a list of all the child components, and | |||||
populate our widget with those. | |||||
[source,java] | |||||
.... | |||||
package com.example.widgetcontainer.client.ui; | |||||
import java.util.List; | |||||
import com.google.gwt.core.client.GWT; | |||||
import com.google.gwt.user.client.ui.Widget; | |||||
import com.example.widgetcontainer.WidgetContainer; | |||||
import com.vaadin.client.ComponentConnector; | |||||
import com.vaadin.client.ConnectorHierarchyChangeEvent; | |||||
import com.vaadin.client.ui.AbstractComponentContainerConnector; | |||||
import com.vaadin.client.ui.Connect; | |||||
@Connect(WidgetContainer.class) | |||||
public class WidgetContainerConnector extends | |||||
AbstractComponentContainerConnector { | |||||
@Override | |||||
public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) { | |||||
List<ComponentConnector> children = getChildComponents(); | |||||
VWidgetContainer widget = getWidget(); | |||||
widget.clear(); | |||||
for (ComponentConnector connector : children) { | |||||
widget.add(connector.getWidget()); | |||||
} | |||||
} | |||||
@Override | |||||
public VWidgetContainer getWidget() { | |||||
return (VWidgetContainer) super.getWidget(); | |||||
} | |||||
public void updateCaption(ComponentConnector connector) { | |||||
} | |||||
} | |||||
.... | |||||
This implementation removes all the component's in the widget and adds | |||||
all that are returned from `getChildComponents`. An obvious optimization | |||||
to these is to compare what is already in the widget and only | |||||
add/remove/move those widgets that have changed. | |||||
[[example-usage]] | |||||
Example Usage | |||||
^^^^^^^^^^^^^ | |||||
Nothing left but to use the component! Compile the widgetset and check | |||||
that the widgetset is defined with the @WidgetSet annotation in the UI class. | |||||
Here is a little stand-alone application that uses this component: | |||||
[source,java] | |||||
.... | |||||
package com.example.widgetcontainer; | |||||
import java.util.Random; | |||||
import com.vaadin.annotations.Widgetset; | |||||
import com.vaadin.terminal.WrappedRequest; | |||||
import com.vaadin.ui.Button; | |||||
import com.vaadin.ui.Button.ClickEvent; | |||||
import com.vaadin.ui.Button.ClickListener; | |||||
import com.vaadin.ui.CheckBox; | |||||
import com.vaadin.ui.Component; | |||||
import com.vaadin.ui.Label; | |||||
import com.vaadin.ui.UI; | |||||
@Widgetset("com.example.widgetcontainer.Widgetset") | |||||
public class WidgetcontainerUI extends UI { | |||||
@Override | |||||
public void init(VaadinRequest request) { | |||||
VerticalLayout layout = new VerticalLayout(); | |||||
layout.setMargin(true); | |||||
setContent(layout); | |||||
Label label = new Label("Hello Vaadin user"); | |||||
layout.addComponent(label); | |||||
final WidgetContainer widgetContainer = new WidgetContainer(); | |||||
layout.addComponent(widgetContainer); | |||||
widgetContainer.addComponent(new Label( | |||||
"Click the button to add components to the WidgetContainer.")); | |||||
Button button = new Button("Add more components", new ClickListener() { | |||||
@Override | |||||
public void buttonClick(ClickEvent event) { | |||||
Random randomGenerator = new Random(); | |||||
int random = randomGenerator.nextInt(3); | |||||
Component component; | |||||
if (random % 3 == 0) { | |||||
component = new Label("A new label"); | |||||
} else if (random % 3 == 1) { | |||||
component = new Button("A button!"); | |||||
} else { | |||||
component = new CheckBox("A textfield"); | |||||
} | |||||
widgetContainer.addComponent(component); | |||||
} | |||||
}); | |||||
layout.addComponent(button); | |||||
} | |||||
} | |||||
.... |
--- | |||||
title: Creating A Theme Using SASS | |||||
order: 53 | |||||
layout: page | |||||
--- | |||||
[[creating-a-theme-using-sass]] | |||||
Creating a theme using SASS | |||||
--------------------------- | |||||
Vaadin 7 comes with built in support for Sass, which can be thought of | |||||
as a preprocessor for CSS. From the Sass homepage: | |||||
_Sass makes CSS fun again. Sass is an extension of CSS3, adding nested | |||||
rules, variables, mixins, selector inheritance, and more._ | |||||
Sass looks like CSS with some added features, and is compiled into CSS | |||||
before being sent to the browser. The compilation is either done | |||||
beforehand, or (during development) on-the-fly by the servlet. | |||||
In Vaadin 7 you can make use of Sass in any of your CSS, and as usual | |||||
there are more than one way to arrange this. The recommended way if you | |||||
do not have a specific reason not to do so, is to compile your theme | |||||
into one CSS file (that is: without any CSS @include), but we'll start | |||||
with the getting-your-feet-wet approach that looks exactly as | |||||
before.It’s worth noting that you can continue to use CSS without Sass | |||||
just as before, if you prefer. | |||||
[[getting-your-feet-wet]] | |||||
Getting your feet wet | |||||
^^^^^^^^^^^^^^^^^^^^^ | |||||
In Vaadin 7 you set the theme in use by specifying the `@Theme` annotation | |||||
on your UI, e.g `@Theme(“themename”)`. Ignoring Sass for a second, you | |||||
would then create a `mytheme/styles.css` that typically `@import` the | |||||
Reindeer theme (in case you forgot, your theme should be located in | |||||
`WebContent/VAADIN/themes/<themename>/styles.css`). You can start using | |||||
Sass with this approach, by renaming your `styles.css` to `styles.scss` and | |||||
importing `legacy-styles.css` instead of `styles.css` - the resulting CSS | |||||
will be exactly as the same as before, BUT now you're free to use Sass | |||||
in your theme: | |||||
[source,scss] | |||||
.... | |||||
@import url(../reindeer/legacy-styles.css); | |||||
$color : green; | |||||
.v-button-caption { | |||||
color: $color; | |||||
} | |||||
.... | |||||
Here we just define a Sass variable to use as color for button captions. | |||||
*NOTE* that this way (using legacy-styles) you still lose one important | |||||
new feature: you can't have multiple themes on the same page when using | |||||
the legacy-styles.css -approach. To gain this feature, which is crucial | |||||
if you intend to run multiple applications with different themes | |||||
embedded in the same page (e.g portals), you must use Sass. | |||||
[[compiling]] | |||||
Compiling | |||||
^^^^^^^^^ | |||||
Provided you’re in development mode (not production), the scss will | |||||
automatically be translated into CSS. You can also compile the scss | |||||
manually (and MUST do so for production). To do this you should run | |||||
`com.vaadin.sass.SassCompiler` with the Vaadin jars on the classpath and | |||||
give it your scss file and output file as arguments. If you have the | |||||
jars readily available, you could do something like this in the command | |||||
line: | |||||
[source,bash] | |||||
.... | |||||
> java -cp '../../../WEB-INF/lib/*' com.vaadin.sass.SassCompiler styles.scss styles.css | |||||
.... | |||||
Another way would be to save the auto-compiled styles.css from the | |||||
browser. | |||||
Support has been added to the Eclipse plugin through the _Compile Vaadin | |||||
Theme_ button . | |||||
NOTE that if you're using Ivy (the default if you're using the Eclipse | |||||
plugin), you must make sure to get the appropriate dependencies on your | |||||
classpath some other way (since they are not present in `WEB-INF/lib`). In | |||||
Eclipse, use the Run -dialog to inherit the classpath from your project. | |||||
You'll notice that the resulting theme still uses `@import` to 'extend' | |||||
the Reindeer theme: | |||||
[source,scss] | |||||
.... | |||||
@import url(../reindeer/legacy-styles.css); | |||||
.... | |||||
This approach is an easy way to get started with Sass, but will cause | |||||
two requests (one for our theme, one for Reindeer). Let’s have a look at | |||||
the recommended approach next. | |||||
[[going-deeper]] | |||||
Going deeper | |||||
^^^^^^^^^^^^ | |||||
Instead of using CSS `@import` to base your application theme on, you can | |||||
(and probably should) use Sass `@import` to make a monolithic theme (one | |||||
CSS file, one request when using the application). Just `@import reindeer.scss`, and `@include` it: | |||||
[source,scss] | |||||
.... | |||||
// mytheme.scss | |||||
@import "../reindeer/reindeer.scss"; | |||||
.mytheme { | |||||
@include reindeer; | |||||
$color : yellow; | |||||
.v-button-caption { | |||||
color: $color; | |||||
} | |||||
} | |||||
.... | |||||
This produces a styles.css that contains all the styles for Reindeer as | |||||
well as your custom styles (note that this makes your final CSS quite | |||||
big to scroll trough, so you might not want to do this when just | |||||
learning the Sass syntax). There is no `@import` in the compiled CSS, so | |||||
it will not cause additional requests. Additionally, due to the way | |||||
Vaadin Sass is structured, this opens up for many possibilities to | |||||
customize, mix-and-match themes, and leave unused stuff out. | |||||
One important thing to notice, is that we wrapped everything in | |||||
`.themename {}`, in this case `.mytheme {}`. This is the magic sauce that | |||||
makes it possible to have multiple themes on one page. _It is crucial | |||||
that the name matches your themename, or your styles will not be | |||||
applied._ | |||||
Some of the nice features you get with Sass include variables, selector | |||||
nesting, mixins (optionally with paramaters), selector inheritance. For | |||||
more information of what you can do with Sass, you should refer to the | |||||
official documentation at http://sass-lang.com | |||||
Please note that the Vaadin Sass compiler only supports the “SCSS”, | |||||
which is the “new main syntax” (the original Sass also supports another, | |||||
older syntax).The Vaadin version aims to be completely compatible, | |||||
though initially there will be some limitations (and actually some added | |||||
functionality). Please let us know if you find something is not working | |||||
as expected. | |||||
[[one-more-thing-recommended-structure]] | |||||
One more thing: Recommended structure | |||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |||||
In Vaadin 7, all Vaadin core themes are using Sass. The | |||||
_reindeer/styles.css_ we included first, is the compiled Reindeer theme, | |||||
including the stuff from the Base theme that Reindeer extends. The Sass | |||||
for the Reindeer theme is in _reindeer/reindeer.scss_, and contains one | |||||
big mixin that will include the whole theme, unless you specifically | |||||
tell it to leave out some parts. The themes are further divided into | |||||
smaller parts, that can be left out, or separately included and renamed | |||||
- providing a powerful way to customize and mix-and-match themes. | |||||
*It is recommended* that you go ahead an divide your own theme into at | |||||
least two files as well: *styles.scss* and *themename.scss* (where | |||||
'themename' is the name of your theme). This is will make your theme | |||||
extendable, and also has the nice benefit that file you usually edit is | |||||
uniquely named (themename.scss) instead of a generic styles.scss that | |||||
you might have many of. | |||||
For a theme named 'mytheme', this would look as follows: | |||||
`mytheme/styles.scss:` | |||||
[source,scss] | |||||
.... | |||||
@import "mytheme.scss"; | |||||
.mytheme { | |||||
@include mytheme; | |||||
} | |||||
.... | |||||
`mytheme/mytheme.scss`: | |||||
[source,scss] | |||||
.... | |||||
@import "../reindeer/reindeer.scss"; | |||||
@mixin mytheme { | |||||
// your styles go here | |||||
@include reindeer; | |||||
} | |||||
.... | |||||
This is the exact structure Vaadin core themes are using, and the way | |||||
the Eclipse plugin will set things up for you (not yet in beta 10). | |||||
Of course, you're still free to arrange your theme in another way if you | |||||
prefer. | |||||
Upcoming tutorials will address specific use-cases! |
--- | |||||
title: Creating A UI Extension | |||||
order: 50 | |||||
layout: page | |||||
--- | |||||
[[creating-a-ui-extension]] | |||||
Creating a UI extension | |||||
----------------------- | |||||
An *Extension* is an entity that is not a full-fledged UI component, but | |||||
is instead used to enhance or extend the functionality of an existing | |||||
component (or connector, more generally.) Unlike components, extensions | |||||
cannot be detached and reattached once they are attached to their | |||||
target. | |||||
Extensions usually consist of a pair of `Connector`{empty}s like components do. | |||||
Hence, they can use the regular shared state and RPC mechanisms to | |||||
communicate between the client and the server. Extensions may or may not | |||||
have a UI. They can create and display widgets on the client side, but | |||||
are not part of the regular layout hierarchy. | |||||
We will rewrite the | |||||
https://vaadin.com/directory/component/refresher[Refresher] add-on as an | |||||
extension. The Refresher causes the client to "ping" the server at | |||||
regular intervals, allowing the server to keep the client up-to-date if | |||||
the application state is changed eg. by a background thread (because of | |||||
the way Vaadin works, the server cannot itself initiate communication.) | |||||
We start by writing the barebones server-side class for our extension: | |||||
[source,java] | |||||
.... | |||||
public class Refresher extends AbstractExtension { | |||||
public Refresher(UI ui) { | |||||
extend(target); | |||||
} | |||||
} | |||||
.... | |||||
Two things to note: | |||||
* If we were writing a component, we would probably want to inherit from | |||||
`AbstractComponent`. Here, we inherit from `AbstractExtension` instead. | |||||
* The connector that should be extended is passed to the constructor, | |||||
which then uses the protected `extend(Connector)` method to attach | |||||
itself to the target connector. In this case it does not make much sense | |||||
attached to individual components, so the constructor only accepts `UI`. | |||||
Next, the Refresher needs an RPC interface to ping the server and a | |||||
shared state to keep track of the interval. These are rather trivial: | |||||
[source,java] | |||||
.... | |||||
public interface RefresherRpc extends ServerRpc { | |||||
public void refresh(); | |||||
} | |||||
.... | |||||
[source,java] | |||||
.... | |||||
public class RefresherState extends SharedState { | |||||
public int interval; | |||||
} | |||||
.... | |||||
The client-side connector is just like a component connector except that | |||||
we inherit from `AbstractExtensionConnector`, not | |||||
`AbstractComponentConnector`. We do not write a client-side widget at | |||||
all, because the Refresher does not have a UI. | |||||
We create a `Timer` instance that calls the `refresh` RPC method when | |||||
run. In `onStateChange()`, we know that either the interval, enabled | |||||
state, or both have changed, so we always cancel a possible | |||||
currently-running timer and schedule a new one if we're enabled. We also | |||||
remember to cancel the timer when the extension is detached. | |||||
[source,java] | |||||
.... | |||||
@Connect(Refresher.class) | |||||
public class RefresherConnector extends AbstractExtensionConnector { | |||||
private Timer timer = new Timer() { | |||||
@Override | |||||
public void run() { | |||||
getRpcProxy(RefresherRpc.class).refresh(); | |||||
} | |||||
}; | |||||
@Override | |||||
public void onStateChanged(StateChangeEvent event) { | |||||
super.onStateChanged(event); | |||||
timer.cancel(); | |||||
if (isEnabled()) { | |||||
timer.scheduleRepeating(getState().interval); | |||||
} | |||||
} | |||||
@Override | |||||
public void onUnregister() { | |||||
timer.cancel(); | |||||
} | |||||
@Override | |||||
protected void extend(ServerConnector target) { | |||||
// Nothing for refresher to do here as it does not need to access the | |||||
// connector it extends | |||||
} | |||||
@Override | |||||
public RefresherState getState() { | |||||
return (RefresherState) super.getState(); | |||||
} | |||||
} | |||||
.... | |||||
Finally, we add an event listener interface and some accessor methods to | |||||
`Refresher`. There is nothing extension-specific in the following code: | |||||
[source,java] | |||||
.... | |||||
public interface RefreshListener { | |||||
static Method METHOD = ReflectTools.findMethod(RefreshListener.class, | |||||
"refresh", RefreshEvent.class); | |||||
public void refresh(RefreshEvent refreshEvent); | |||||
} | |||||
public class RefreshEvent extends EventObject { | |||||
public RefreshEvent(Refresher refresher) { | |||||
super(refresher); | |||||
} | |||||
public Refresher getRefresher() { | |||||
return (Refresher) getSource(); | |||||
} | |||||
} | |||||
public Refresher(UI ui) { | |||||
registerRpc(new RefresherRpc() { | |||||
@Override | |||||
public void refresh() { | |||||
fireEvent(new RefreshEvent(Refresher.this)); | |||||
} | |||||
}); | |||||
extend(ui); | |||||
} | |||||
@Override | |||||
public RefresherState getState() { | |||||
return (RefresherState) super.getState(); | |||||
} | |||||
public void setInterval(int millis) { | |||||
getState().interval = millis; | |||||
} | |||||
public int getInterval() { | |||||
return getState().interval; | |||||
} | |||||
public void setEnabled(boolean enabled) { | |||||
getState().enabled = enabled; | |||||
} | |||||
public boolean isEnabled() { | |||||
return getState().enabled; | |||||
} | |||||
public void addRefreshListener(RefreshListener listener) { | |||||
super.addListener(RefreshEvent.class, listener, RefreshListener.METHOD); | |||||
} | |||||
public void removeRefreshListener(RefreshListener listener) { | |||||
super.removeListener(RefreshEvent.class, listener, | |||||
RefreshListener.METHOD); | |||||
} | |||||
.... |
--- | |||||
title: Customizing Component Theme With SASS | |||||
order: 47 | |||||
layout: page | |||||
--- | |||||
[[customizing-component-theme-with-sass]] | |||||
Customizing component theme with SASS | |||||
------------------------------------- | |||||
In addition to the general benefits Sass brings to the world of CSS in | |||||
Vaadin 7, the way themes are set up allows us to quite easily accomplish | |||||
some things that were previously hard. | |||||
Let’s start from the top, without Sass, and continue from there. We'll | |||||
use the new _setPrimaryStyleName()_ to do some things previously not | |||||
possible. | |||||
We’ll work on a small example with buttons that we want to customize: | |||||
[source,java] | |||||
.... | |||||
@Theme("sassy") | |||||
public class SassyUI extends UI { | |||||
@Override | |||||
public void init(VaadinRequest request) { | |||||
Button b = new Button("Reindeer"); | |||||
Layout layout = new VerticalLayout(); | |||||
layout.addComponent(b); | |||||
setContent(layout); | |||||
} | |||||
} | |||||
.... | |||||
And our basic (mostly empty at this point) “sassy” theme, based on | |||||
Reindeer, looks like this (assuming you're using the recommended | |||||
styles.scss+themename.scss structure as introduced in the previous | |||||
tutorial): | |||||
[source,scss] | |||||
.... | |||||
@import "../reindeer/reindeer.scss"; | |||||
@mixin sassy { | |||||
@include reindeer; | |||||
// your styles go here | |||||
} | |||||
.... | |||||
And the result is a basic Reindeer-looking button. We can change the | |||||
color of the caption like this: | |||||
[source,scss] | |||||
.... | |||||
.v-button-caption { | |||||
color: red; | |||||
} | |||||
.... | |||||
…but this changes ALL buttons. We just want some of the buttons to stand | |||||
out: | |||||
[source,java] | |||||
.... | |||||
b = new Button("important"); | |||||
b.addStyleName("important"); | |||||
layout.addComponent(b); | |||||
.... | |||||
css: | |||||
[source,scss] | |||||
.... | |||||
.important .v-button-caption { | |||||
color: red; | |||||
} | |||||
.... | |||||
Ok, this is all fine - but we realize our important button should | |||||
actually not look at all like a Reindeer button. | |||||
Since Reindeer adds quite a few styles, this requires quite a lot of | |||||
customization with this approach. Enter _setPrimaryStyleName()_: | |||||
[source,java] | |||||
.... | |||||
b = new Button("More important"); | |||||
b.setPrimaryStyleName("my-button"); | |||||
addComponent(b); | |||||
.... | |||||
Now everything that was previously _.v-button_ in the browser DOM is all | |||||
of a sudden _.my-button_, and we have a completely unstyled button, but | |||||
with the DOM-structure and functionality of a regular button. We can | |||||
easily style this without interference from theme styles: | |||||
[source,scss] | |||||
.... | |||||
.my-button { | |||||
color: red; | |||||
} | |||||
.... | |||||
However, in our case we realize we still want it to look like a button, | |||||
just not with so much decorations as a Reindeer button. Let’s apply Base | |||||
styles: | |||||
[source,scss] | |||||
.... | |||||
@include base-button($primaryStyleName: my-button); | |||||
.my-button { | |||||
color: red; | |||||
} | |||||
.... | |||||
What? We now have a basic button with red text, but how? | |||||
We have @included base-button and renamed it’s selectors to “my-button” | |||||
(instead of the default “v-button”). This makes the rules match our | |||||
button perfectly (we used setPrimaryStyleName() to rename it) - in | |||||
effect we apply base-button to our “my-button”. | |||||
Now we have a good starting-point. Note that this might not be such a | |||||
big deal for small things, like buttons, but imagine something like | |||||
Table witout _any_ styles. Yikes. | |||||
Here are the full sources (using distinct colors for each button for | |||||
clarity): | |||||
[source,java] | |||||
.... | |||||
package com.example.sassy; | |||||
import com.vaadin.annotations.Theme; | |||||
import com.vaadin.server.VaadinRequest; | |||||
import com.vaadin.ui.Button; | |||||
import com.vaadin.ui.Layout; | |||||
import com.vaadin.ui.UI; | |||||
import com.vaadin.ui.VerticalLayout; | |||||
@Theme("sassy") | |||||
public class SassyUI extends UI { | |||||
@Override | |||||
public void init(VaadinRequest request) { | |||||
Button b = new Button("Reindeer"); | |||||
Layout layout = new VerticalLayout(); | |||||
layout.addComponent(b); | |||||
b = new Button("important"); | |||||
b.addStyleName("important"); | |||||
layout.addComponent(b); | |||||
b = new Button("More important"); | |||||
b.setPrimaryStyleName("my-button"); | |||||
layout.addComponent(b); | |||||
setContent(layout); | |||||
} | |||||
} | |||||
.... | |||||
[source,scss] | |||||
.... | |||||
// sassy/styles.scss | |||||
@import "sassy.scss"; | |||||
.sassy { | |||||
@include sassy; | |||||
} | |||||
.... | |||||
[source,scss] | |||||
.... | |||||
// sassy/sassy.scss | |||||
@import "../reindeer/reindeer.scss"; | |||||
@mixin sassy { | |||||
@include reindeer; | |||||
.v-button-caption { | |||||
color: red; | |||||
} | |||||
.important .v-button-caption { | |||||
color: green; | |||||
} | |||||
@include base-button($name: my-button); | |||||
.my-button { | |||||
color: blue; | |||||
} | |||||
} | |||||
.... |
--- | |||||
title: Customizing The Startup Page In An Application | |||||
order: 43 | |||||
layout: page | |||||
--- | |||||
[[customizing-the-startup-page-in-an-application]] | |||||
Customizing the startup page in an application | |||||
---------------------------------------------- | |||||
In Vaadin 6, the startup page - used to bootstrap a new Vaadin UI | |||||
instance in the browser - was generated as a monolithic chunk of HTML | |||||
and was not easily customizable. In Vaadin 7, we added a new facility | |||||
for registering special _bootstrap listeners_ that are invoked before | |||||
the bootstrap response is sent. In addition, instead of bare HTML in a | |||||
string, the response is now built as a DOM tree that is easy to | |||||
manipulate programmatically. | |||||
Here's an example of a simple bootstrap listener: | |||||
[source,java] | |||||
.... | |||||
import org.jsoup.nodes.Comment; | |||||
import org.jsoup.nodes.Element; | |||||
import org.jsoup.nodes.Node; | |||||
import org.jsoup.parser.Tag; | |||||
// ... | |||||
new BootstrapListener() { | |||||
@Override | |||||
public void modifyBootstrapPage(BootstrapPageResponse response) { | |||||
response.getDocument().body().appendChild(new Comment("Powered by Vaadin!", "")); | |||||
} | |||||
@Override | |||||
public void modifyBootstrapFragment(BootstrapFragmentResponse response) { | |||||
// Wrap the fragment in a custom div element | |||||
Element myDiv = new Element(Tag.valueOf("div"), ""); | |||||
List<Node> nodes = response.getFragmentNodes(); | |||||
for(Node node : nodes) { | |||||
myDiv.appendChild(node); | |||||
} | |||||
nodes.clear(); | |||||
nodes.add(myDiv); | |||||
} | |||||
} | |||||
.... | |||||
The HTML library we use is http://jsoup.org/[jsoup]. It provides a very | |||||
convenient API for traversing, manipulating and extracting data from a | |||||
DOM, and is HTML5 compliant. | |||||
The `BootstrapListener` interface contains two methods, one of which is | |||||
usually left empty. This is because a Vaadin application can be either | |||||
stand-alone, in which case it "owns" the whole page its UI resides in, | |||||
or embedded, such as a portlet, in which case it does not control the | |||||
content of the page it is embedded in. | |||||
The `modifyBootstrapFragment` method is called in both cases. It | |||||
receives a `BootstrapFragmentResponse` that represents the HTML fragment | |||||
that is inserted in the host page, whether the page is controlled by | |||||
Vaadin or not. Hence, you only need to implement this method if you do | |||||
not care about the host page, whether your application is embedded or | |||||
standalone. | |||||
The `modifyBootstrapPage` method is called with a | |||||
`BootstrapPageResponse` argument that represents the whole bootstrap | |||||
page, including the fragment mentioned above. Thus, it is only invoked | |||||
when the application is standalone and actually responsible for | |||||
generating the page. This method allows you to, for instance, add things | |||||
to the `head` element. The `BootstrapPageResponse` class also allows | |||||
setting arbitrary HTTP response headers: | |||||
[source,java] | |||||
.... | |||||
public void modifyBootstrapPage(BootstrapPageResponse response) { | |||||
response.setHeader("X-Powered-By", "Vaadin 7"); | |||||
} | |||||
.... | |||||
But how and where should the bootstrap listeners be registered? It | |||||
should be only once per session, and right in the beginning, so that | |||||
they are already added when the first response is sent. | |||||
To do that you should write a custom servlet that extends | |||||
`VaadinServlet`, or a custom portlet extending `VaadinPortlet`, and a | |||||
session init listener that adds the bootstrap listener to the new | |||||
session. | |||||
[source,java] | |||||
.... | |||||
class MyVaadinServlet extends VaadinServlet { | |||||
@Override | |||||
protected void servletInitialized() throws ServletException { | |||||
super.servletInitialized(); | |||||
getService().addSessionInitListener(new SessionInitListener() { | |||||
@Override | |||||
public void sessionInit(SessionInitEvent event) { | |||||
event.getSession().addBootstrapListener(listener); | |||||
} | |||||
}); | |||||
} | |||||
} | |||||
// Or... | |||||
class MyVaadinPortlet extends VaadinPortlet { | |||||
@Override | |||||
protected void portletInitialized() throws PortletException { | |||||
super.portletInitialized(); | |||||
getService().addSessionInitListener(new SessionInitListener() { | |||||
@Override | |||||
public void sessionInit(SessionInitEvent event) { | |||||
event.getSession().addBootstrapListener(listener); | |||||
} | |||||
}); | |||||
} | |||||
} | |||||
.... |
--- | |||||
title: Exposing Server Side API To JavaScript | |||||
order: 41 | |||||
layout: page | |||||
--- | |||||
[[exposing-server-side-api-to-javascript]] | |||||
Exposing server-side API to JavaScript | |||||
-------------------------------------- | |||||
The new JavaScript integration functionality will allow you to easily | |||||
publish methods that can be called with JavaScript on the client side. | |||||
In effect, you can publish a JavaScript API for your application. | |||||
Although you will probably not find yourself using this very often, it | |||||
can be useful when integrating with JavaScript frameworks or embedding | |||||
within legacy sites. | |||||
Exposing a `notify()` method that takes a message and displays that as a | |||||
notification can be done in one simple block in e.g `UI.init()`: | |||||
[source,java] | |||||
.... | |||||
JavaScript.getCurrent().addFunction("notify", new JavaScriptFunction() { | |||||
public void call(JSONArray arguments) throws JSONException { | |||||
Notification.show(arguments.getString(0)); | |||||
} | |||||
}); | |||||
.... | |||||
This will expose the `notify()`{empty}-method globally in the window object. | |||||
Technically it's thus `window.notify()`, but you can call it by simply | |||||
by `notify()`. Try entering `notify("Hey!")` into the Firebug or | |||||
Developler Tools console, or `javascript:notify("Hey!")` into the | |||||
address bar. | |||||
You'll notice that this assumes there is a String in the first position | |||||
of the array. Also, this will clutter the global namespace, which is | |||||
generally not a good idea, unless you really have a specific need for | |||||
that. | |||||
Let's make a complete example with two arguments, some simple error | |||||
handling, and namespacing: | |||||
[source,java] | |||||
.... | |||||
JavaScript.getCurrent().addFunction("com.example.api.notify", | |||||
new JavaScriptFunction() { | |||||
public void call(JSONArray arguments) throws JSONException { | |||||
try { | |||||
String caption = arguments.getString(0); | |||||
if (arguments.length() == 1) { | |||||
// only caption | |||||
Notification.show(caption); | |||||
} else { | |||||
// type should be in [1] | |||||
Notification.show(caption, | |||||
Type.values()[arguments.getInt(1)]); | |||||
} | |||||
} catch (JSONException e) { | |||||
// We'll log in the console, you might not want to | |||||
JavaScript.getCurrent().execute( | |||||
"console.error('" + e.getMessage() + "')"); | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
.... | |||||
Using the dotted notation for the method will automatically create those | |||||
objects in the browser; you'll call this method like so: | |||||
`com.example.api.notify("Hey!")`. You do not have to use a long name | |||||
like this, though - it's up to you and your use-case. | |||||
The second thing to notice is that we now wrapped the code in a | |||||
try-catch, so that the wrong number or wrong types of arguments does not | |||||
cause an ugly stacktrace in our server logs. Again, how you should react | |||||
to erroneous use of your exposed API depends on your use-case. We'll log | |||||
an error message to the browser console as an example. | |||||
We're now accepting a second (integer) argument, and using that as | |||||
_type_ for the `Notification`. | |||||
Finally, we'll add a link that will call the function, and work as a | |||||
_Bookmarklet_. You can drag the link to your bookmarks bar, and when you | |||||
invoke it when viewing the application with our exposed `notify()`{empty}-method, you will be prompted for a message that will then be sent to | |||||
the method. Here is the plain HTML code for creating such a link: | |||||
[source,html] | |||||
.... | |||||
<a href="javascript:(function(){com.example.api.notify(prompt('Message'),2);})();">Send message</a> | |||||
.... | |||||
Here is the full source for our application: | |||||
[source,java] | |||||
.... | |||||
import org.json.JSONArray; | |||||
import org.json.JSONException; | |||||
import com.vaadin.server.ExternalResource; | |||||
import com.vaadin.server.VaadinRequest; | |||||
import com.vaadin.ui.JavaScript; | |||||
import com.vaadin.ui.JavaScriptFunction; | |||||
import com.vaadin.ui.Link; | |||||
import com.vaadin.ui.Notification; | |||||
import com.vaadin.ui.Notification.Type; | |||||
import com.vaadin.ui.UI; | |||||
public class JSAPIUI extends UI { | |||||
@Override | |||||
public void init(VaadinRequest request) { | |||||
JavaScript.getCurrent().addFunction("com.example.api.notify", | |||||
new JavaScriptFunction() { | |||||
public void call(JSONArray arguments) throws JSONException { | |||||
try { | |||||
String caption = arguments.getString(0); | |||||
if (arguments.length() == 1) { | |||||
// only caption | |||||
Notification.show(caption); | |||||
} else { | |||||
// type should be in [1] | |||||
Notification.show(caption, | |||||
Type.values()[arguments.getInt(1)]); | |||||
} | |||||
} catch (JSONException e) { | |||||
// We'll log in the console, you might not want to | |||||
JavaScript.getCurrent().execute( | |||||
"console.error('" + e.getMessage() + "')"); | |||||
} | |||||
} | |||||
}); | |||||
setContent(new Link( | |||||
"Send message", | |||||
new ExternalResource( | |||||
"javascript:(function(){com.example.api.notify(prompt('Message'),2);})();"))); | |||||
} | |||||
} | |||||
.... |
^^^^^^^^^^^^ | ^^^^^^^^^^^^ | ||||
Putting all these pieces together, we end up with this class that uses | Putting all these pieces together, we end up with this class that uses | ||||
the same data as in the link:UsingGridWithAContainer.asciidoc[Using | |||||
Grid with a Container] example. | |||||
the same data as in the <<UsingGridWithAContainer#using-with-a-container,Using | |||||
Grid with a Container>> example. | |||||
[source,java] | [source,java] | ||||
.... | .... |
--- | |||||
title: Integrating A JavaScript Component | |||||
order: 38 | |||||
layout: page | |||||
--- | |||||
[[integrating-a-javascript-component]] | |||||
Integrating a JavaScript component | |||||
---------------------------------- | |||||
You can use an existing JavaScript component as a component in Vaadin by | |||||
creating a server-side API for the component as well as writing the | |||||
JavaScript code that connects the server-side API to the actual | |||||
JavaScript component. Because of the dynamic nature of JavaScript, you | |||||
don't need to use GWT development mode or recompile the widgetset while | |||||
making client-side changes. | |||||
The server-side component should extend `AbstractJavaScriptComponent` and | |||||
provide the API that the developer uses to interact with the component. | |||||
The class should also have a `@JavaScript` annotation that defines the | |||||
required JavaScript libraries in the order they should be loaded. This | |||||
example uses the Flot graph library from http://code.google.com/p/flot/. | |||||
Float requires jQuery which is loaded using | |||||
https://developers.google.com/speed/libraries/[Google Libraries API]. | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.annotations.*; | |||||
@JavaScript({"https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js", "jquery.flot.js", "flot_connector.js"}) | |||||
public class Flot extends AbstractJavaScriptComponent { | |||||
public void addSeries(double... points) { | |||||
List<List<Double>> pointList = new ArrayList<List<Double>>(); | |||||
for (int i = 0; i < points.length; i++) { | |||||
pointList.add(Arrays.asList(Double.valueOf(i), | |||||
Double.valueOf(points[i]))); | |||||
} | |||||
getState().series.add(pointList); | |||||
} | |||||
@Override | |||||
public FlotState getState() { | |||||
return (FlotState) super.getState(); | |||||
} | |||||
} | |||||
.... | |||||
The shared state class will not be used by any GWT code so you don't | |||||
have to put it in the widgetset's client package. The state class should | |||||
extend `JavaScriptComponentState` but is otherwise similar to the shared | |||||
state of a normal GWT component. | |||||
[source,java] | |||||
.... | |||||
public class FlotState extends JavaScriptComponentState { | |||||
public List<List<List<Double>>> series = new ArrayList<List<List<Double>>>(); | |||||
} | |||||
.... | |||||
The only remaining code is the client-side JavaScript connector in | |||||
`flot_connector.js`. The connector defines a global initializer function | |||||
named based on the fully qualified name of the server-side `Component` | |||||
class with dots replaced with underscores. In this example the | |||||
server-side `Component` is `com.example.Flot` which means that the function | |||||
name should be `com_example_Flot`. | |||||
This initializer function should initialize the JavaScript part of the | |||||
component. It is called by the framework with `this` pointing to a | |||||
connector wrapper providing integration to the framework. For full | |||||
information about the services provided by the connector wrapper, please | |||||
read the Javadoc for the `AbstractJavaScriptComponent` class. | |||||
In this example, the initializer first initializes the `element` | |||||
variable with a jQuery object for the DOM element of the component. | |||||
Next, a state change listener is defined by assigning a function to the | |||||
`onStateChange` field of the connector wrapper. This function will be | |||||
called whenever the shared state is changed from the server-side code. | |||||
In the state change listener, the Flot API is used to initialize a graph | |||||
with the data series from the shared state into the DOM element. | |||||
The format of the series property in the `FlotState` Java class has been | |||||
chosen with the Flot API in mind. Flot expects an array of data series | |||||
where each item is an array of data points where each data point is an | |||||
array with the x value followed by the y value. This is defined in Java | |||||
as `List<List<List<Double>>>` and then the framework takes care of the | |||||
conversion between server-side Java values and client-side JavaScript | |||||
values. `double[][][]` in Java would give the same JavaScript structure, | |||||
but it was not used here as it gives less flexibility in the Java code. | |||||
[source,javascript] | |||||
.... | |||||
window.com_example_Flot = function() { | |||||
var element = $(this.getElement()); | |||||
this.onStateChange = function() { | |||||
$.plot(element, this.getState().series); | |||||
} | |||||
} | |||||
.... | |||||
By implementing a server-side Java class extending | |||||
`AbstractJavaScriptConnector` and a client-side JavaScript connector | |||||
initialization function, existing JavaScript component libraries can | |||||
easily be integrated to Vaadin. The server-side code is almost similar | |||||
to the code required for a component based on GWT and the client-side | |||||
code is quite similar to a `ComponentConnector` implemented using GWT. | |||||
[WARNING] | |||||
.Security Warning | |||||
==== | |||||
Do note that third-party JavaScript code could be dangerous | |||||
(https://www.owasp.org/index.php/3rd_Party_Javascript_Management_Cheat_Sheet), | |||||
and you should take into account the security risks of using such. | |||||
==== |
--- | |||||
title: Integrating A JavaScript Library As An Extension | |||||
order: 39 | |||||
layout: page | |||||
--- | |||||
[[integrating-a-javascript-library-as-an-extension]] | |||||
Integrating a JavaScript library as an extension | |||||
------------------------------------------------ | |||||
JavaScript can also be used for creating Extensions e.g. for integrating | |||||
existing JavaScript libraries. See <<CreatingAUIExtension#creating-a-ui-extension, | |||||
Creating a UI extension>> for general information about Extensions. The main | |||||
difference when using JavaScript is that you extend | |||||
`AbstractJavaScriptExtension`, that your shared state class should | |||||
extend `JavaScriptExtensionState` and then of course that your | |||||
client-side implementation is written in JavaScript. See | |||||
<<IntegratingAJavaScriptComponent#integrating-a-javascript-component, | |||||
Integrating a JavaScript component>> for basic information about how to use | |||||
JavaScript for your client-side logic. | |||||
This tutorial will create a simple Extension for integrating | |||||
https://developers.google.com/analytics/devguides/collection/gajs/[Google | |||||
Analytics]. Because the Analytics API just uses the same `_gaq.push` | |||||
function with different arguments, the JavaScript connector logic can be | |||||
equally simple. Aside from asynchronously loading ga.js, the client-side | |||||
code just adds a callback that the server-side code can use to push new | |||||
commands. | |||||
[source,javascript] | |||||
.... | |||||
window._gaq = window._gaq || []; | |||||
(function() { | |||||
var ga = document.createElement('script'); | |||||
ga.type = 'text/javascript'; | |||||
ga.async = true; | |||||
ga.src = ('https:' == document.location.protocol ? | |||||
'https://ssl' : 'http://www') + | |||||
'.google-analytics.com/ga.js'; | |||||
var s = document.getElementsByTagName('script')[0]; | |||||
s.parentNode.insertBefore(ga, s); | |||||
})(); | |||||
window.com_example_Analytics = function() { | |||||
this.pushCommand = function(command) { | |||||
_gaq.push(command); | |||||
} | |||||
} | |||||
.... | |||||
The server-side Extension class provides the common Extension API for | |||||
extending a UI instance as well as API for some Analytics features. All | |||||
the Analytics features are based on the `pushCommand` method that | |||||
invokes the corresponding client-side callback. | |||||
The Analytics API used in this example has nothing that warrants using | |||||
shared state, but you can of course use shared state in your own | |||||
JavaScript Extension if you want to as long as your state class extends | |||||
`JavaScriptExtensionState`. | |||||
[source,java] | |||||
.... | |||||
@JavaScript("analytics_connector.js") | |||||
public class Analytics extends AbstractJavaScriptExtension { | |||||
public Analytics(UI ui, String account) { | |||||
extend(ui); | |||||
pushCommand("_setAccount", account); | |||||
} | |||||
public void trackPageview(String name) { | |||||
pushCommand("_trackPageview", name); | |||||
} | |||||
private void pushCommand(Object... commandAndArguments) { | |||||
// Cast to Object to use Object[] commandAndArguments as the first | |||||
// varargs argument instead of as the full varargs argument array. | |||||
callFunction("pushCommand", (Object) commandAndArguments); | |||||
} | |||||
} | |||||
.... | |||||
Extensions are suitable for integrating many existing JavaScript | |||||
libraries that do not provide a component that is added to a layout. By | |||||
using a client-side JavaScript connector for integrating the JavaScript | |||||
library, you can eliminate GWT from the equation to give you slightly | |||||
less code to maintain. | |||||
[WARNING] | |||||
.Security Warning | |||||
==== | |||||
Do note that third-party JavaScript code can be dangerous | |||||
(https://www.owasp.org/index.php/3rd_Party_Javascript_Management_Cheat_Sheet), | |||||
and you should take into account the security risks of using such. | |||||
==== |
= Migrating from Vaadin 6 to Vaadin 7 | = Migrating from Vaadin 6 to Vaadin 7 | ||||
For migration to Vaadin 7.1, see | For migration to Vaadin 7.1, see | ||||
link:MigratingFromVaadin7.0ToVaadin7.1.asciidoc[Migrating | |||||
from Vaadin 7.0 to Vaadin 7.1] | |||||
<<MigratingFromVaadin7.0ToVaadin7.1#migrating-from-vaadin-7.0-to-vaadin-7.1, | |||||
Migrating From Vaadin 7.0 To Vaadin 7.1>> | |||||
[[getting-started]] | [[getting-started]] | ||||
Getting Started | Getting Started | ||||
Label label = new Label("Hello Vaadin!"); | Label label = new Label("Hello Vaadin!"); | ||||
mainWindow.addComponent(label); | mainWindow.addComponent(label); | ||||
setMainWindow(mainWindow); | setMainWindow(mainWindow); | ||||
setTheme(“mytheme”); | |||||
setTheme("mytheme"); | |||||
} | } | ||||
} | } | ||||
.... | .... | ||||
layout* and addComponent() etc. As this often caused confusion and | layout* and addComponent() etc. As this often caused confusion and | ||||
caused layout problems when unaware of the implicit layout or forgetting | caused layout problems when unaware of the implicit layout or forgetting | ||||
to set its layout parameters, Vaadin 7 now requires *explicitly setting | to set its layout parameters, Vaadin 7 now requires *explicitly setting | ||||
the content*. See See e.g. | |||||
link:CreatingABasicApplication.asciidoc[Creating | |||||
a basic application] | |||||
the content*. See e.g. | |||||
<<CreatingABasicApplication#creating-a-basic-application,Creating | |||||
a basic application>> | |||||
If you want to minimize the impact of this on the look and theme of an | If you want to minimize the impact of this on the look and theme of an | ||||
old application, you can reproduce the *old structure* simply by setting | old application, you can reproduce the *old structure* simply by setting | ||||
To customize the creation of UIs - for instance to create different UIs | To customize the creation of UIs - for instance to create different UIs | ||||
for mobile and desktop devices - | for mobile and desktop devices - | ||||
*link:CreatingAnApplicationWithDifferentFeaturesForDifferentClients.asciidoc[a | |||||
custom UIProvider]* can be used. | |||||
<<CreatingAnApplicationWithDifferentFeaturesForDifferentClients#creating- | |||||
an-application-with-different-features-for-different-clients,a | |||||
custom UIProvider>> can be used. | |||||
[[forms-and-data-binding]] | [[forms-and-data-binding]] | ||||
Forms and Data Binding | Forms and Data Binding | ||||
* *FieldGroup* supporting *automated data binding*, whether for a hand-designed | * *FieldGroup* supporting *automated data binding*, whether for a hand-designed | ||||
form or | form or | ||||
link:AutoGeneratingAFormBasedOnABeanVaadin6StyleForm.asciidoc[creating | |||||
the fields automatically] | |||||
<<AutoGeneratingAFormBasedOnABeanVaadin6StyleForm# | |||||
auto-generating-a-form-based-on-a-bean-vaadin-6-style,creating the fields automatically>> | |||||
* *link:CreatingATextFieldForIntegerOnlyInputUsingADataSource.asciidoc[typed | |||||
fields and properties]* | |||||
* *<<CreatingATextFieldForIntegerOnlyInputWhenNotUsingADataSource# | |||||
creating-a-textfield-for-integer-only-input-when-not-using-a-data-source,typed | |||||
fields and properties>>* | |||||
* *link:CreatingYourOwnConverterForString.asciidoc[converters]*, | |||||
* *<<CreatingYourOwnConverterForString#creating-your-own-converter-for-string-mytype-conversion, | |||||
converters>>*, | |||||
both | both | ||||
link:ChangingTheDefaultConvertersForAnApplication.asciidoc[automatic | |||||
via ConverterFactory] and | |||||
link:CreatingATextFieldForIntegerOnlyInputWhenNotUsingADataSource.asciidoc[explicitly set] | |||||
<<ChangingTheDefaultConvertersForAnApplication#changing-the-default-converters-for-an-application, | |||||
automatic via ConverterFactory>> and | |||||
<<CreatingATextFieldForIntegerOnlyInputWhenNotUsingADataSource# | |||||
creating-a-textfield-for-integer-only-input-when-not-using-a-data-source,explicitly set>> | |||||
* improved *validation* (performed on data model values after | * improved *validation* (performed on data model values after | ||||
conversion) - see e.g. | conversion) - see e.g. | ||||
link:UsingBeanValidationToValidateInput.asciidoc[bean validation example] | |||||
<<UsingBeanValidationToValidateInput#using-bean-validation-to-validate-input,bean validation example>> | |||||
* and more | * and more | ||||
If you have optimized your widgetset to limit what components to load | If you have optimized your widgetset to limit what components to load | ||||
initially, see | initially, see | ||||
link:OptimizingTheWidgetSet.asciidoc[this | |||||
tutorial] and the | |||||
<<OptimizingTheWidgetSet#optimizing-the-widget-set,this tutorial>> and the | |||||
https://vaadin.com/directory/component/widget-set-optimizer[WidgetSet | https://vaadin.com/directory/component/widget-set-optimizer[WidgetSet | ||||
Optimizer add-on]. | Optimizer add-on]. | ||||
selector. | selector. | ||||
To take advantage of the new features, see | To take advantage of the new features, see | ||||
link:CreatingAThemeUsingSass.asciidoc[Creating | |||||
a theme using sass] and | |||||
link:CustomizingComponentThemeWithSass.asciidoc[Customizing | |||||
component theme with Sass]. | |||||
<<CreatingAThemeUsingSass#creating-a-theme-using-sass,Creating a theme using Sass>> | |||||
and | |||||
<<CustomizingComponentThemeWithSass#customizing-component-theme-with-sass, | |||||
Customizing component theme with Sass>>. | |||||
Note that the SCSS theme needs to be *compiled* to CSS before use - in | Note that the SCSS theme needs to be *compiled* to CSS before use - in | ||||
development mode, this takes place automatically on the fly whenever the | development mode, this takes place automatically on the fly whenever the | ||||
theme is loaded, but when moving to production mode, you need to run the | theme is loaded, but when moving to production mode, you need to run the | ||||
theme compiler on it to produce a pre-compiled static theme. | theme compiler on it to produce a pre-compiled static theme. | ||||
link:WidgetStylingUsingOnlyCSS.asciidoc[CSS | |||||
can be used to style components] somewhat more freely than in Vaadin 6. | |||||
<<WidgetStylingUsingOnlyCSS#widget-styling-using-only-css,CSS can be used to style | |||||
components>> somewhat more freely than in Vaadin 6. | |||||
The DOM structure of several layouts has changed, which might require | The DOM structure of several layouts has changed, which might require | ||||
changes to themes for layouts. See also the section on layouts below. | changes to themes for layouts. See also the section on layouts below. | ||||
The best way to get acquainted with the new navigation features is to | The best way to get acquainted with the new navigation features is to | ||||
check the tutorials on | check the tutorials on | ||||
link:CreatingABookmarkableApplicationWithBackButtonSupport.asciidoc[creating | |||||
a bookmarkable application], | |||||
link:UsingParametersWithViews.asciidoc[using | |||||
parameters with views], | |||||
link:AccessControlForViews.asciidoc[access | |||||
control for views] and | |||||
link:ViewChangeConfirmations.asciidoc[view | |||||
change confirmations]. | |||||
<<CreatingABookmarkableApplicationWithBackButtonSupport# | |||||
creating-a-bookmarkable-application-with-back-button-support, | |||||
creating a bookmarkable application>>, | |||||
<<UsingParametersWithViews#using-parameters-with-views,using parameters with views>>, | |||||
<<AccessControlForViews#access-control-for-views,access control for views>> and | |||||
<<ViewChangeConfirmations#view-change-confirmations,view change confirmations>>. | |||||
When logging out a user, you can use *Page.setLocation()* to redirect | When logging out a user, you can use *Page.setLocation()* to redirect | ||||
the user to a suitable page. | the user to a suitable page. | ||||
The most common customizations: | The most common customizations: | ||||
* link:CustomizingTheStartupPageInAnApplication.asciidoc[Customizing | |||||
the bootstrap page]: JavaScript, headers, ... | |||||
* <<CustomizingTheStartupPageInAnApplication#customizing-the-startup-page-in-an-application, | |||||
Customizing the bootstrap page in an application>>: JavaScript, headers, ... | |||||
* Add-ons using customized servlets for other purposes (e.g. customizing | * Add-ons using customized servlets for other purposes (e.g. customizing | ||||
communication between client and server) probably need more extensive | communication between client and server) probably need more extensive | ||||
rework | rework | ||||
off into LabelConnector. The annotations linking the client side and the | off into LabelConnector. The annotations linking the client side and the | ||||
server side have also changed, now the LabelConnector has an *@Connect* | server side have also changed, now the LabelConnector has an *@Connect* | ||||
annotation linking it to the server side component Label. | annotation linking it to the server side component Label. | ||||
https://vaadin.com/book/vaadin7/-/page/architecture.client-side.html[the | |||||
https://vaadin.com/book/vaadin7/-/page/architecture.client-side.html[The | |||||
book] provides some background and the tutorial on | book] provides some background and the tutorial on | ||||
link:CreatingASimpleComponent.asciidoc[creating | |||||
a simple component] shows an example. | |||||
<<CreatingASimpleComponent#creating-a-simple-component,creating a simple | |||||
component>> shows an example. | |||||
The connector communicates with the server primarily via shared | The connector communicates with the server primarily via shared | ||||
state from the server to the client and **RPC | state from the server to the client and **RPC | ||||
calls **link:SendingEventsFromTheClientToTheServerUsingRPC.asciidoc[from | |||||
client to server] and | |||||
link:UsingRPCToSendEventsToTheClient.asciidoc[from | |||||
server to client], with a larger set of supported data types. For | |||||
calls **<<SendingEventsFromTheClientToTheServerUsingRPC# | |||||
sending-events-from-the-client-to-the-server-using-RPC,from | |||||
client to server>> and | |||||
<<UsingRPCToSendEventsToTheClient#using-rpc-to-send-events-to-the-client, | |||||
from server to client>>, with a larger set of supported data types. For | |||||
component containers, | component containers, | ||||
link:CreatingASimpleComponentContainer.asciidoc[the | |||||
hierarchy of the contained components is sent separately]. | |||||
<<CreatingASimpleComponentContainer#creating-a-simple-component-container, | |||||
the hierarchy of the contained components is sent separately>>. | |||||
The old mechanism with UIDL, *paintContent()* and *changeVariables()* is | The old mechanism with UIDL, *paintContent()* and *changeVariables()* is | ||||
still there for a while to ease migration, but it is recommended to | still there for a while to ease migration, but it is recommended to | ||||
There are also new features such as support for *Extensions* (components | There are also new features such as support for *Extensions* (components | ||||
which | which | ||||
link:CreatingAUIExtension.asciidoc[extend | |||||
the UI] or | |||||
link:CreatingAComponentExtension.asciidoc[other | |||||
components] without having a widget in a layout) and | |||||
link:UsingAJavaScriptLibraryOrAStyleSheetInAnAddOn.asciidoc[support | |||||
for JavaScript], also for | |||||
link:IntegratingAJavaScriptComponent.asciidoc[implementing | |||||
components] and | |||||
link:IntegratingAJavaScriptLibraryAsAnExtension.asciidoc[extensions], | |||||
<<CreatingAUIExtension#creating-a-ui-extension,extend the UI>> or | |||||
<<CreatingAComponentExtension#creating-a-component-extension,other | |||||
components>> without having a widget in a layout) and | |||||
<<UsingAJavaScriptLibraryOrAStyleSheetInAnAddOn# | |||||
using-a-javascript-library-or-a-style-sheet-in-an-addon,support for | |||||
JavaScript>>, also for | |||||
<<IntegratingAJavaScriptComponent#integrating-a-javascript-component, | |||||
implementing components>> and | |||||
<<IntegratingAJavaScriptLibraryAsAnExtension# | |||||
integrating-a-javascript-library-as-an-extension,extensions>>, | |||||
which might simplify the implementation of some components. Shared state | which might simplify the implementation of some components. Shared state | ||||
and RPC can also be used from JavaScript, and there are other techniques | and RPC can also be used from JavaScript, and there are other techniques | ||||
for client-server communication. | for client-server communication. | ||||
Among the changes affecting others than layout developers, *CssLayout* | Among the changes affecting others than layout developers, *CssLayout* | ||||
now consists of a single DIV instead of three nested elements, and | now consists of a single DIV instead of three nested elements, and | ||||
link:WidgetStylingUsingOnlyCSS.asciidoc[CSS | |||||
can be used to do more customization] than in previous Vaadin versions. | |||||
<<WidgetStylingUsingOnlyCSS#widget-styling-using-only-css,CSS | |||||
can be used to do more customization>> than in previous Vaadin versions. | |||||
Also other layouts have changed in terms of their *DOM structure* on the | Also other layouts have changed in terms of their *DOM structure* on the | ||||
client, which might require changes to themes. The interface | client, which might require changes to themes. The interface | ||||
*MarginHandler* is now only implemented by layouts that actually support | *MarginHandler* is now only implemented by layouts that actually support | ||||
For those implementing new component containers or layouts, see the | For those implementing new component containers or layouts, see the | ||||
related tutorials | related tutorials | ||||
link:CreatingASimpleComponentContainer.asciidoc[Creating | |||||
a simple component container] and | |||||
link:WidgetStylingUsingOnlyCSS.asciidoc[Widget | |||||
styling using only CSS]. | |||||
<<CreatingASimpleComponentContainer#creating-a-simple-component-container, | |||||
Creating a simple component container>> and | |||||
<<WidgetStylingUsingOnlyCSS#widget-styling-using-only-css, | |||||
Widget styling using only CSS>>. | |||||
[[migration-steps-for-componentcontainers]] | [[migration-steps-for-componentcontainers]] | ||||
Migration steps for ComponentContainers | Migration steps for ComponentContainers |
Migrating from Vaadin 6 | Migrating from Vaadin 6 | ||||
~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~ | ||||
When migrating from Vaadin 6, first review | |||||
link:MigratingFromVaadin6ToVaadin7.asciidoc[Migrating | |||||
from Vaadin 6 to Vaadin 7], then continue with the rest of this guide. | |||||
When migrating from Vaadin 6, first review | |||||
<<MigratingFromVaadin6ToVaadin7#migrating-from-vaadin-6-to-vaadin-7,Migrating | |||||
from Vaadin 6 to Vaadin 7>>, then continue with the rest of this guide. | |||||
[[migrating-from-vaadin-7.0]] | [[migrating-from-vaadin-7.0]] | ||||
Migrating from Vaadin 7.0 | Migrating from Vaadin 7.0 |
--- | |||||
title: Optimizing The Widget Set | |||||
order: 44 | |||||
layout: page | |||||
--- | |||||
[[optimizing-the-widget-set]] | |||||
Optimizing the widget set | |||||
------------------------- | |||||
Vaadin contains a lot of components and most of those components | |||||
contains a client side part which is executed in the browser. Together | |||||
all the client side implementations sum up to a big amount of data the | |||||
enduser needs to download to the browser even if he might never use all | |||||
the components. | |||||
For that reason Vaadin uses three strategies for downloading the | |||||
components: | |||||
[[eager]] | |||||
Eager | |||||
+++++ | |||||
What eager means is that the client implementation for the component is | |||||
included in the payload that is initially downloaded when the | |||||
application starts. The more components that is made eager the more will | |||||
need to be downloaded before the initial view of the application is | |||||
shown. By default Vaadin puts most components here since Vaadin does not | |||||
know which components will be used in the first view and cannot thus | |||||
optimize any further. You would have noticed this if you ever made a | |||||
Hello World type of application and wondered why Vaadin needed to | |||||
download so much for such a simple application. | |||||
[[deferred]] | |||||
Deferred | |||||
++++++++ | |||||
When marking a component as deferred it means that its client side | |||||
implementation will be downloaded right after the initial rendering of | |||||
the application is done. This can be useful if you know for instance | |||||
that a component will soon be used in that application but is not | |||||
displayed in the first view. | |||||
[[lazy]] | |||||
Lazy | |||||
++++ | |||||
Lazy components client side implementation doesn't get downloaded until | |||||
the component is shown. This will sometimes mean that a view might take | |||||
a bit longer to render if several components client side implementation | |||||
needs to first be downloaded. This strategy is useful for components | |||||
that are rarely used in the application which everybody might not see. | |||||
`RichTextArea` and `ColorPicker` are examples of components in Vaadin that by | |||||
default are Lazy. | |||||
[[optimizing-the-loading-of-the-widgets]] | |||||
Optimizing the loading of the widgets | |||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |||||
Now that we know what Vaadin provides, lets see how we can modify how a | |||||
component is loaded to provide the best experience for our application. | |||||
Lets say we want to build a HelloWorld application which only needs a | |||||
few components. Specifically these components will be shown on the | |||||
screen: | |||||
* UI - The UI of the application. | |||||
* VerticalLayout - The Vertical layout inside the UI where the message | |||||
is shown | |||||
* Label - A label with the text "Hello World" | |||||
All other Vaadin components provided by Vaadin we don't want to load. To | |||||
do this we are going to mark those three components as Eager (initially | |||||
loaded) and all the rest as Lazy. | |||||
To do that we need to implement our own `ConnectorBundleLoaderFactory`. | |||||
Here is my example one: | |||||
[source,java] | |||||
.... | |||||
public class MyConnectorBundleLoaderFactory extends | |||||
ConnectorBundleLoaderFactory { | |||||
private static final List<Class> eagerComponents = new | |||||
LinkedList<Class>(); | |||||
static { | |||||
eagerComponents.add(UI.class); | |||||
eagerComponents.add(VerticalLayout.class); | |||||
eagerComponents.add(Label.class); | |||||
} | |||||
@Override protected LoadStyle getLoadStyle(JClassType connectorType){ | |||||
Connect annotation = connectorType.getAnnotation(Connect.class); | |||||
Class componentClass = annotation.value(); | |||||
// Load eagerly marked connectors eagerly | |||||
if(eagerComponents.contains(componentClass)) { | |||||
return LoadStyle.EAGER; | |||||
} | |||||
//All other components should be lazy | |||||
return LoadStyle.LAZY; | |||||
} | |||||
} | |||||
.... | |||||
We also need to add our factory to the widgetset by adding the following | |||||
to our <widgetset>.gwt.xml: | |||||
[source,xml] | |||||
.... | |||||
<generate-with class="com.example.widgetsetoptimization.MyConnectorBundleLoaderFactory"> | |||||
<when-type-assignable class="com.vaadin.client.metadata.ConnectorBundleLoader" /> | |||||
</generate-with> | |||||
.... | |||||
If you are using the Eclipse Plugin to compile the widgetset you will | |||||
also want to add the following meta data for the compiler so it does not | |||||
overwrite our generator setting: | |||||
[source,xml] | |||||
.... | |||||
<!-- WS Compiler: manually edited --> | |||||
.... | |||||
If you have used the Maven archetype for setting up your project, you | |||||
might need to add vaadin-client-compiler as a dependency in your project | |||||
as it is by default only used when actually starting the widgetset | |||||
compiler. See http://dev.vaadin.com/ticket/11533 for more details. | |||||
Finally, here is my simple test application UI for which I have | |||||
optimized the widgetset: | |||||
[source,java] | |||||
.... | |||||
public class HelloWorldUI extends UI { | |||||
@Override | |||||
protected void init(VaadinRequest request) { | |||||
VerticalLayout layout = new VerticalLayout(); | |||||
layout.addComponent(new Label("Hello world")); | |||||
setContent(layout); | |||||
} | |||||
} | |||||
.... | |||||
Now, all I have to do is recompile the widgetset for the new load | |||||
strategy to take effect. | |||||
If you now check the network traffic when you load the application you | |||||
will notice a *huge difference*. Using the default widgetset with the | |||||
default loading strategy our Hello World application will load over *1 | |||||
Mb* of widgetset data. If you then switch to using our own widgetset | |||||
with our own custom loader factory the widgetset will only be about *375 | |||||
kb*. That is over *60% less!* | |||||
Using your own custom widgetset loader factory is highly recommended in | |||||
all projects. | |||||
[[finding-out-which-components-are-loaded-by-a-view]] | |||||
Finding out which components are loaded by a View | |||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |||||
So you want to start optimizing your widgetset but how do you find out | |||||
which components are needed for the initial view so you can make them | |||||
eager while keeping everything else deferred or lazy? Fortunately there | |||||
is an addon | |||||
https://vaadin.com/directory#addon/widget-set-optimizer[WidgetSetOptimizer] | |||||
for doing just this. | |||||
To use it you download this addon and add it to your project. | |||||
Add the following to the <widgetset>.gwt.xml: | |||||
[source,xml] | |||||
.... | |||||
<inherits name="org.vaadin.artur.widgetsetoptimizer.WidgetSetOptimizerWidgetSet" /> | |||||
.... | |||||
You will also need to add the following to your UI classes init method | |||||
[source,java] | |||||
.... | |||||
new WidgetSetOptimizer().extend(this); | |||||
.... | |||||
Finally compile the widgetset and run the application with the &debug | |||||
parameter. In the debug window there will be a new button "OWS" which by | |||||
pressing you will get the Generator class automatically generated for | |||||
you. The generated generator class will mark the currently displayed | |||||
components as Eager while loading everything else as Deferred. More | |||||
information about the addon and its usage can be found on the Addon page | |||||
in the directory. |
--- | |||||
title: Sending Events From The Client To The Server Using RPC | |||||
order: 37 | |||||
layout: page | |||||
--- | |||||
[[sending-events-from-the-client-to-the-server-using-RPC]] | |||||
Sending events from the client to the server using RPC | |||||
------------------------------------------------------ | |||||
An RPC mechanism can be used to communicate from the client to the | |||||
server. In effect, the client can call methods that are executed by the | |||||
server component. The server component can then take appropriate action | |||||
- e.g updating the shared state or calling event listeners. | |||||
To set up client-server RPC we need to create one interface defining the | |||||
RPC methods, and then make use of that interface on both the client and | |||||
the server. Place the `MyComponentServerRpc` interface in the client | |||||
package: | |||||
[source,java] | |||||
.... | |||||
package com.example.mycomponent.client; | |||||
import com.vaadin.terminal.gwt.client.MouseEventDetails; | |||||
import com.vaadin.terminal.gwt.client.communication.ServerRpc; | |||||
public interface MyComponentServerRpc extends ServerRpc { | |||||
public void clicked(MouseEventDetails mouseDetails); | |||||
} | |||||
.... | |||||
Note that the RPC methods can not have return values. In this example, | |||||
we pass `MouseEventDetails` to get a more complete example, but you | |||||
could pass almost any (or no) parameters. | |||||
In the server side `MyComponent` we need to implement the interface, and | |||||
register it for use: | |||||
[source,java] | |||||
.... | |||||
package com.example.mycomponent; | |||||
import com.example.mycomponent.client.MyComponentServerRpc; | |||||
import com.example.mycomponent.client.MyComponentState; | |||||
import com.vaadin.terminal.gwt.client.MouseEventDetails; | |||||
import com.vaadin.ui.AbstractComponent; | |||||
public class MyComponent extends AbstractComponent { | |||||
private int clickCount = 0; | |||||
private MyComponentServerRpc rpc = new MyComponentServerRpc() { | |||||
public void clicked(MouseEventDetails mouseDetails) { | |||||
clickCount++; | |||||
setText("You have clicked " + clickCount + " times"); | |||||
} | |||||
}; | |||||
public MyComponent() { | |||||
registerRpc(rpc); | |||||
} | |||||
/* Previous code commented out for clarity: | |||||
@Override | |||||
public MyComponentState getState() { | |||||
return (MyComponentState) super.getState(); | |||||
} | |||||
public void setText(String text) { | |||||
getState().text = text; | |||||
} | |||||
public String getText() { | |||||
return getState().text; | |||||
} | |||||
*/ | |||||
} | |||||
.... | |||||
Here we react to the RPC call by incrementing a counter. We do not make | |||||
use of the `MouseEventDetails` (yet). Notice the *important call to | |||||
`registerRpc()`* in the added constructor. | |||||
In the client side `MyComponentConnector`, we use `RpcProxy` to get an | |||||
implementation of the RPC interface, and call the `clicked()` method | |||||
when the widget is clicked: | |||||
[source,java] | |||||
.... | |||||
package com.example.mycomponent.client; | |||||
// imports removed for clarity | |||||
import com.vaadin.terminal.gwt.client.communication.RpcProxy; | |||||
@Connect(MyComponent.class) | |||||
public class MyComponentConnector extends AbstractComponentConnector { | |||||
MyComponentServerRpc rpc = RpcProxy | |||||
.create(MyComponentServerRpc.class, this); | |||||
public MyComponentConnector() { | |||||
getWidget().addClickHandler(new ClickHandler() { | |||||
public void onClick(ClickEvent event) { | |||||
final MouseEventDetails mouseDetails = MouseEventDetailsBuilder | |||||
.buildMouseEventDetails(event.getNativeEvent(), | |||||
getWidget().getElement()); | |||||
rpc.clicked(mouseDetails); | |||||
} | |||||
}); | |||||
} | |||||
/* Previous code commented out for clarity: | |||||
@Override | |||||
protected Widget createWidget() { | |||||
return GWT.create(MyComponentWidget.class); | |||||
} | |||||
@Override | |||||
public MyComponentWidget getWidget() { | |||||
return (MyComponentWidget) super.getWidget(); | |||||
} | |||||
@Override | |||||
public MyComponentState getState() { | |||||
return (MyComponentState) super.getState(); | |||||
} | |||||
@OnStateChange("text") | |||||
void updateText() { | |||||
getWidget().setText(getState().text); | |||||
} | |||||
*/ | |||||
} | |||||
.... | |||||
Notice that most of the code is for attaching the click handler and | |||||
creating the `MouseEventDetails`, the code for the actual RPC is quite | |||||
minimal. | |||||
Compile the widgetset, and the label text should be updated with the | |||||
click count whenever you click it (remember that the counting is done on | |||||
the server-side). | |||||
Finally, note that you can use multiple RPC interfaces in one component, | |||||
allowing for better code separation and reuse. | |||||
You can do the same thing in the other direction, see the article about | |||||
server to client RPC for details. |
a row. | a row. | ||||
This example uses the same data as in the | This example uses the same data as in the | ||||
link:UsingGridWithAContainer.asciidoc[Using Grid with a Container] | |||||
example. | |||||
<<UsingGridWithAContainer#using-grid-with-a-container, | |||||
Using Grid with a Container>> example. | |||||
[[detailsgenerator]] | [[detailsgenerator]] | ||||
DetailsGenerator | DetailsGenerator |
= Simplified RPC using JavaScript | = Simplified RPC using JavaScript | ||||
This tutorial continues where | This tutorial continues where | ||||
link:IntegratingAJavaScriptComponent.asciidoc[Integrating a JavaScript | |||||
component] ended. We will now add RPC functionality to the JavaScript | |||||
Flot component. RPC can be used in the same way as with ordinary GWT | |||||
components as described in link:UsingRPCFromJavaScript.asciidoc[Using | |||||
RPC from JavaScript]. This tutorial describes a simplified way that is | |||||
<<IntegratingAJavaScriptComponent#integrating-a-javascript-component, | |||||
Integrating a JavaScript component>> ended. We will now add RPC | |||||
functionality to the JavaScript Flot component. RPC can be used in the | |||||
same way as with ordinary GWT components as described in | |||||
<<IntegratingAJavaScriptComponent#integrating-a-javascript-component, | |||||
Using RPC from JavaScript>>. This tutorial describes a simplified way that is | |||||
based on the same concepts as in | based on the same concepts as in | ||||
link:ExposingServerSideAPIToJavaScript.asciidoc[Exposing server | |||||
side API to JavaScript]. This way of doing RPC is less rigorous and is | |||||
intended for simple cases and for developers appreciating the dynamic | |||||
nature of JavaScript. | |||||
<<ExposingServerSideAPIToJavaScript#exposing-server-side-api-to-javascript, | |||||
Exposing server side API to JavaScript>>. This way of doing RPC is less | |||||
rigorous and is intended for simple cases and for developers appreciating | |||||
the dynamic nature of JavaScript. | |||||
The simplified way is based on single callback functions instead of | The simplified way is based on single callback functions instead of | ||||
interfaces containing multiple methods. We will invoke a server-side | interfaces containing multiple methods. We will invoke a server-side |
--- | |||||
title: Using A JavaScript Library Or A Style Sheet In An Add On | |||||
order: 40 | |||||
layout: page | |||||
--- | |||||
[[using-a-javascript-library-or-a-style-sheet-in-an-addon]] | |||||
Using a JavaScript library or a style sheet in an add-on | |||||
-------------------------------------------------------- | |||||
Including style sheets or JavaScript files in your add-ons or as a part | |||||
of your application can now be done by adding a `@StyleSheet` or | |||||
`@JavaScript` annotation to a `Component` or `Extension` class. Each | |||||
annotation takes a list of strings with URLs to the resources that | |||||
should be loaded on the page before the framework initializes the | |||||
client-side `Component` or `Extension`. | |||||
The URLs can either be complete absolute urls (e.g. https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js) or | |||||
relative URLs (e.g. _redbutton.css_). A relative URL is converted to a | |||||
special URL that will download the file from the Java package where the | |||||
defining class is located. This means that e.g. | |||||
`@StyleSheet({"redbutton.css"})` on the class `com.example.RedButton` will | |||||
cause the file `com/example/redbutton.css` on the classpath to be loaded | |||||
in the browser. `@JavaScript` works in exactly the same way - see | |||||
<<IntegratingAJavaScriptComponent#integrating-a-javascript-component, | |||||
Integrating a JavaScript component>> for a practical example. | |||||
[source,java] | |||||
.... | |||||
@StyleSheet("redbutton.css") | |||||
public class RedButton extends NativeButton { | |||||
public RedButton(String caption) { | |||||
super(caption); | |||||
addStyleName("redButton"); | |||||
} | |||||
} | |||||
.... | |||||
In this simple example, the `RedButton` component just adds a `redButton` | |||||
style name to a normal `NativeButton`. _redbutton.css_ is located in the | |||||
same folder as _RedButton.java_ and has this content: | |||||
[source,css] | |||||
.... | |||||
.redButton { | |||||
background-color: red; | |||||
} | |||||
.... | |||||
This new mechanism makes it very easy to include style sheet or | |||||
JavaScript files with add-ons and automatically load them in the browser | |||||
when the add-on is used. | |||||
[WARNING] | |||||
.Security Warning | |||||
==== | |||||
Do note that third-party JavaScript code can be dangerous | |||||
(https://www.owasp.org/index.php/3rd_Party_Javascript_Management_Cheat_Sheet), | |||||
and you should take into account the security risks of using such. | |||||
==== |
--- | |||||
title: Using Bean Validation To Validate Input | |||||
order: 45 | |||||
layout: page | |||||
--- | |||||
[[using-bean-validation-to-validate-input]] | |||||
Using Bean Validation to validate input | |||||
--------------------------------------- | |||||
Before you get started with Bean Validation you need to download a Bean | |||||
Validation implementation and add it to your project. You can find one | |||||
for instance at http://bval.apache.org/downloads.html. Just add the jars | |||||
from the lib folder to your project. | |||||
Bean Validation works as a normal validator. If you have a bean with | |||||
Bean Validation annotations, such as: | |||||
[source,java] | |||||
.... | |||||
public class Person { | |||||
@Size(min = 5, max = 50) | |||||
private String name; | |||||
@Min(0) | |||||
@Max(100) | |||||
private int age; | |||||
// + constructor + setters + getters | |||||
} | |||||
.... | |||||
You can create a field for the name field as you always would: | |||||
[source,java] | |||||
.... | |||||
Person person = new Person("John", 26); | |||||
BeanItem<Person> item = new BeanItem<Person>(person); | |||||
TextField firstName = new TextField("First name", | |||||
item.getItemProperty("name")); | |||||
firstName.setImmediate(true); | |||||
setContent(firstName); | |||||
.... | |||||
and add the bean validation as a normal validator: | |||||
[source,java] | |||||
.... | |||||
firstName.addValidator(new BeanValidator(Person.class, "name")); | |||||
.... | |||||
Your `firstName` field is now automatically validated based on the | |||||
annotations in your bean class. You can do the same thing for the `age` | |||||
field and you won't be able to set a value outside the valid 0-100 | |||||
range. | |||||
A Bean Validation tutorial is available here: | |||||
http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html |
= Using Grid with inline data | = Using Grid with inline data | ||||
Instead of using a Vaadin Container as explained in | Instead of using a Vaadin Container as explained in | ||||
link:UsingGridWithAContainer.asciidoc[Using Grid with a Container], | |||||
<<UsingGridWithAContainer# | |||||
using-grid-with-a-container,Using Grid With a Container>>, | |||||
you can also directly add simple inline data to Grid without directly | you can also directly add simple inline data to Grid without directly | ||||
using a Container. | using a Container. | ||||
--- | |||||
title: Using Parameters With Views | |||||
order: 36 | |||||
layout: page | |||||
--- | |||||
[[using-parameters-with-views]] | |||||
Using parameters with Views | |||||
--------------------------- | |||||
When the Navigator API is in use, one can pass "parameters" to Views in | |||||
the URI fragment. | |||||
The remainder of the fragment that is left after the (longest) view name | |||||
matched is removed, is considered to be "fragment parameters". These are | |||||
passed to the View in question, which can then handle the parameter(s). | |||||
Basically: `#!viewname/parameters`. | |||||
Continuing from the basic navigation example, let's make a View that | |||||
displays a message passed as a fragment parameter: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.View; | |||||
import com.vaadin.ui.Label; | |||||
import com.vaadin.ui.Panel; | |||||
public class MessageView extends Panel implements View { | |||||
public static final String NAME = "message"; | |||||
public MessageView() { | |||||
super(new VerticalLayout()); | |||||
setCaption("Messages"); | |||||
} | |||||
@Override | |||||
public void enter(ViewChangeEvent event) { | |||||
if(event.getParameters() != null){ | |||||
// split at "/", add each part as a label | |||||
String[] msgs = event.getParameters().split("/"); | |||||
for (String msg : msgs) { | |||||
((Layout)getContent()).addComponent(new Label(msg)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
.... | |||||
Let's register `MessageView` along with the other Views: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.Navigator; | |||||
import com.vaadin.navigator.Navigator.SimpleViewDisplay; | |||||
import com.vaadin.server.Page; | |||||
import com.vaadin.server.WrappedRequest; | |||||
import com.vaadin.ui.UI; | |||||
public class NavigationtestUI extends UI { | |||||
@Override | |||||
public void init(VaadinRequest request) { | |||||
// Create Navigator, make it control the ViewDisplay | |||||
Navigator navigator = new Navigator(this, this); | |||||
// Add some Views | |||||
navigator.addView(MainView.NAME, new MainView()); // no fragment | |||||
// #!count will be a new instance each time we navigate to it, counts: | |||||
navigator.addView(CountView.NAME, CountView.class); | |||||
// #!message adds a label with whatever it receives as a parameter | |||||
navigator.addView(MessageView.NAME, new MessageView()); | |||||
} | |||||
} | |||||
.... | |||||
Finally, we'll add two labels to the MainView so we don't have to type | |||||
in the browsers address-bar to try it out: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.View; | |||||
import com.vaadin.server.ExternalResource; | |||||
import com.vaadin.ui.Link; | |||||
import com.vaadin.ui.Panel; | |||||
public class MainView extends Panel implements View { | |||||
public static final String NAME = ""; | |||||
public MainView() { | |||||
VerticalLayout layout = new VerticalLayout(); | |||||
Link lnk = new Link("Count", new ExternalResource("#!" + CountView.NAME)); | |||||
layout.addComponent(lnk); | |||||
lnk = new Link("Message: Hello", new ExternalResource("#!" | |||||
+ MessageView.NAME + "/Hello")); | |||||
layout.addComponent(lnk); | |||||
lnk = new Link("Message: Bye", new ExternalResource("#!" | |||||
+ MessageView.NAME + "/Bye/Goodbye")); | |||||
layout.addComponent(lnk); | |||||
setContent(layout); | |||||
} | |||||
@Override | |||||
public void enter(ViewChangeEvent event) { | |||||
} | |||||
} | |||||
.... | |||||
Simple! Let's just conclude by noting that it's usually a good idea to | |||||
make sure the parameters are URI encoded, or the browser might | |||||
disapprove. |
--- | |||||
title: Using RPC From JavaScript | |||||
order: 42 | |||||
layout: page | |||||
--- | |||||
[[using-rpc-from-javascript]] | |||||
Using RPC from JavaScript | |||||
------------------------- | |||||
This tutorial continues where | |||||
<<IntegratingAJavaScriptComponent#integrating-a-javascript-component, | |||||
Integrating a JavaScript component>> ended. We will now add RPC | |||||
functionality to the JavaScript Flot component. RPC can be used in the | |||||
same way as with ordinary GWT components. | |||||
We will add RPC from the client to the server when the user clicks a | |||||
data point in the graph and RPC from server to client for highlighting a | |||||
data point in the graph. For each of these, we define an RPC interface. | |||||
Each interface has one method that takes a data series index and the | |||||
index of a point in that series. As with the shared state, the GWT code | |||||
doesn't need to know about these interfaces and it's thus not required | |||||
to put them in the widgetset's client package and to recompile the | |||||
widgetset after making changes. | |||||
[source,java] | |||||
.... | |||||
public interface FlotClickRpc extends ServerRpc { | |||||
public void onPlotClick(int seriesIndex, int dataIndex); | |||||
} | |||||
public interface FlotHighlightRpc extends ClientRpc { | |||||
public void highlight(int seriesIndex, int dataIndex); | |||||
} | |||||
.... | |||||
The server side code for this looks the same as if the client-side | |||||
connector was implemented using GWT. An RPC implementation is registered | |||||
in the constructor. | |||||
[source,java] | |||||
.... | |||||
public Flot() { | |||||
registerRpc(new FlotClickRpc() { | |||||
public void onPlotClick(int seriesIndex, int dataIndex) { | |||||
Notification.show("Clicked on [" + seriesIndex + ", " | |||||
+ dataIndex + "]"); | |||||
} | |||||
}); | |||||
} | |||||
.... | |||||
Highlighting is implemented by getting an RPC proxy object and invoking | |||||
the method. | |||||
[source,java] | |||||
.... | |||||
public void highlight(int seriesIndex, int dataIndex) { | |||||
getRpcProxy(FlotHighlightRpc.class).highlight(seriesIndex, dataIndex); | |||||
} | |||||
.... | |||||
The JavaScript connector uses similar functions from the connector | |||||
wrapper: `this.getRpcProxy()` for getting an object with functions that | |||||
will call the server-side counterpart and `this.registerRpc()` for | |||||
registering an object with functions that will be called from the | |||||
server. Because of the dynamic nature of JavaScript, you don't need to | |||||
use the interface names if you don't want to - all methods from all RPC | |||||
interfaces registered for the connector on the server will be available | |||||
in the RPC proxy object and any RPC method invoked from the server will | |||||
be called if present in the RPC object you registered. If a connector | |||||
uses multiple RPC interfaces that define methods with conflicting names, | |||||
you can still use the interface names to distinguish between interfaces. | |||||
We need to make some small adjustments to the connector JavaScript to | |||||
make it work with the way Flot processes events. Because a new Flot | |||||
object is created each time the `onStateChange` function is called, we | |||||
need to store a reference to the current object that we can use for | |||||
applying the highlight. We also need to pass a third parameter to | |||||
`$.plot` to make the graph area clickable. Aside from those changes, we | |||||
just call the function on the RPC proxy in a click listener and register | |||||
an RPC implementation with a function that highlights a point. | |||||
[source,javascript] | |||||
.... | |||||
window.com_example_Flot = function() { | |||||
var element = $(this.getElement()); | |||||
var rpcProxy = this.getRpcProxy(); | |||||
var flot; | |||||
this.onStateChange = function() { | |||||
flot = $.plot(element, this.getState().series, {grid: {clickable: true}}); | |||||
} | |||||
element.bind('plotclick', function(event, point, item) { | |||||
if (item) { | |||||
rpcProxy.onPlotClick(item.seriesIndex, item.dataIndex); | |||||
} | |||||
}); | |||||
this.registerRpc({ | |||||
highlight: function(seriesIndex, dataIndex) { | |||||
if (flot) { | |||||
flot.highlight(seriesIndex, dataIndex); | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
.... | |||||
When the normal Vaadin RPC is used with JavaScript connectors, you can | |||||
use the same server-side code that you would use with a GWT connector | |||||
and the client-side code uses the same concepts as for GWT connectors, | |||||
just translated to fit into the world of JavaScript. |
--- | |||||
title: Using RPC To Send Events To The Client | |||||
order: 56 | |||||
layout: page | |||||
--- | |||||
[[using-rpc-to-send-events-to-the-client]] | |||||
Using RPC to send events to the client | |||||
-------------------------------------- | |||||
An RPC mechanism can be used to communicate from the server to the | |||||
client. In effect, the server-side component can call methods that are | |||||
executed by the client-side connector. As opposed to shared state | |||||
(discussed in a separate article), no information is automatically | |||||
re-transmitted when the client-side state is lost (e.g when a browser | |||||
reload is invoked). | |||||
Whether shared state or RPC is appropriate depends on the nature of the | |||||
data being transmitted, but if the information transmitted needs to be | |||||
retained on the client over a page refresh, you should probably use | |||||
shared state. You'll probably find shared state more appropriate in most | |||||
cases, and server-client RPC extremely useful in a few cases. | |||||
To set up server-client RPC, we need to create an interface extending | |||||
`ClientRpc` for the RPC methods, then register an implementation of the | |||||
RPC interface in the client-side connector, and call the method(s) via a | |||||
proxy on the server. This is the reverse of the server-client RPC | |||||
described in a separate article. | |||||
We'll create *MyComponentClientRpc* in the client package: | |||||
[source,java] | |||||
.... | |||||
package com.example.mycomponent.client; | |||||
import com.vaadin.client.communication.ClientRpc; | |||||
public interface MyComponentClientRpc extends ClientRpc { | |||||
public void alert(String message); | |||||
} | |||||
.... | |||||
Again, note that the RPC methods can not return anything, but can take | |||||
multiple arguments. | |||||
In *MyComponentConnector* we register the RPC implementation in the | |||||
constructor. This time we'll create the implementation inline: | |||||
[source,java] | |||||
.... | |||||
package com.example.mycomponent.client; | |||||
// imports removed for clarity | |||||
@Connect(MyComponent.class) | |||||
public class MyComponentConnector extends AbstractComponentConnector { | |||||
MyComponentServerRpc rpc = RpcProxy | |||||
.create(MyComponentServerRpc.class, this); | |||||
public MyComponentConnector() { | |||||
registerRpc(MyComponentClientRpc.class, new MyComponentClientRpc() { | |||||
public void alert(String message) { | |||||
Window.alert(message); | |||||
} | |||||
}); | |||||
/* The rest of the code remains unchanged: | |||||
getWidget().addClickHandler(new ClickHandler() { | |||||
public void onClick(ClickEvent event) { | |||||
final MouseEventDetails mouseDetails = MouseEventDetailsBuilder | |||||
.buildMouseEventDetails(event.getNativeEvent(), | |||||
getWidget().getElement()); | |||||
rpc.clicked(mouseDetails); | |||||
} | |||||
}); | |||||
} | |||||
@Override | |||||
protected Widget createWidget() { | |||||
return GWT.create(MyComponentWidget.class); | |||||
} | |||||
@Override | |||||
public MyComponentWidget getWidget() { | |||||
return (MyComponentWidget) super.getWidget(); | |||||
} | |||||
@Override | |||||
public MyComponentState getState() { | |||||
return (MyComponentState) super.getState(); | |||||
} | |||||
@OnStateChange("text") | |||||
void updateText() { | |||||
getWidget().setText(getState().text); | |||||
} | |||||
*/ | |||||
} | |||||
.... | |||||
(`MyComponentServerRpc` is introduced in | |||||
<<SendingEventsFromTheClientToTheServerUsingRPC# | |||||
sending-events-from-the-client-to-the-server-using-RPC, | |||||
Sending events from the client to the server using RPC>>. `Window` here is | |||||
`com.google.gwt.user.client.Window`, _not_ `com.vaadin.ui.Window`.) | |||||
Finally, in *MyComponent* we use the RPC via a proxy: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.ui.AbstractComponent; | |||||
public class MyComponent extends AbstractComponent { | |||||
private int clickCount = 0; | |||||
private MyComponentServerRpc rpc = new MyComponentServerRpc() { | |||||
public void clicked(MouseEventDetails mouseDetails) { | |||||
clickCount++; | |||||
// nag every 5:th click | |||||
if (clickCount % 5 == 0) { | |||||
getRpcProxy(MyComponentClientRpc.class).alert( | |||||
"Ok, that's enough!"); | |||||
} | |||||
setText("You have clicked " + clickCount + " times"); | |||||
} | |||||
}; | |||||
/* Unchanged code follows: | |||||
public MyComponent() { | |||||
registerRpc(rpc); | |||||
} | |||||
@Override | |||||
public MyComponentState getState() { | |||||
return (MyComponentState) super.getState(); | |||||
} | |||||
public void setText(String text) { | |||||
getState().text = text; | |||||
} | |||||
public String getText() { | |||||
return getState().text; | |||||
} | |||||
*/ | |||||
} | |||||
.... | |||||
That is: every fifth time the label is clicked, we get the RPC proxy by | |||||
calling `getRpcProxy()` and call our `alert()` method with a message to | |||||
send to the client. | |||||
Compile the widgetset, and you're all set to try out server-client RPC. |
hierarchical container components along with information from Vaadin's | hierarchical container components along with information from Vaadin's | ||||
book of examples to create the code below (http://demo.vaadin.com/book-examples-vaadin7/book#component.tree.itemstylegenerator). | book of examples to create the code below (http://demo.vaadin.com/book-examples-vaadin7/book#component.tree.itemstylegenerator). | ||||
See link:img/DmoOrgTreeUI.java[DmoOrgTreeUI.java] for full source code. | |||||
See <<img/DmoOrgTreeUI.java,DmoOrgTreeUI.java>> for full source code. | |||||
[source,java] | [source,java] | ||||
.... | .... |
--- | |||||
title: View Change Confirmations | |||||
order: 54 | |||||
layout: page | |||||
--- | |||||
[[view-change-confirmations]] | |||||
View change confirmations | |||||
------------------------- | |||||
The `Navigator` API provides ways to prevent the user from navigating away | |||||
from a view in some cases, usually when the view has some unsaved | |||||
changes. We'll make a simple example that does just that (and only | |||||
that). | |||||
We'll create our simple `SettingsView` later, because it has the actual | |||||
meat of this example. Let's set up the basic stuff first, our UI and our | |||||
`MainView`. | |||||
UI: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.Navigator; | |||||
import com.vaadin.navigator.ViewChangeListener; | |||||
import com.vaadin.server.VaadinRequest; | |||||
import com.vaadin.ui.UI; | |||||
public class NavigationtestUI extends UI { | |||||
@Override | |||||
public void init(VaadinRequest request) { | |||||
// Create Navigator, make it control the ViewDisplay | |||||
Navigator navigator = new Navigator(this, this); | |||||
// no fragment for main view | |||||
navigator.addView(MainView.NAME, new MainView(navigator)); | |||||
// #!settings | |||||
navigator.addView(SettingsView.NAME, new SettingsView(navigator)); | |||||
} | |||||
} | |||||
.... | |||||
Minimalistic. The only thing to notice is that we pass the `Navigator` to | |||||
the `SettingsView`, so that it can attach a listener and trigger | |||||
navigation. More on that when we actually create the `SettingsView`. | |||||
Let's do the `MainView`: | |||||
[source,java] | |||||
.... | |||||
import com.vaadin.navigator.View; | |||||
import com.vaadin.server.ExternalResource; | |||||
import com.vaadin.ui.Link; | |||||
import com.vaadin.ui.Panel; | |||||
public class MainView extends Panel implements View { | |||||
public static final String NAME = ""; | |||||
public MainView(final Navigator navigator) { | |||||
Link lnk = new Link("Settings", new ExternalResource("#!" | |||||
+ SettingsView.NAME)); | |||||
setContent(lnk); | |||||
} | |||||
@Override | |||||
public void enter(ViewChangeEvent event) { | |||||
} | |||||
} | |||||
.... | |||||
Yeah, really nothing to see here - we just create this so we can | |||||
navigate back and forth when trying it out. | |||||
Now let's do the SettingsView, which has some more things going on in | |||||
order to make it fairly complete: | |||||
[source,java] | |||||
.... | |||||
import java.util.Date; | |||||
import com.vaadin.data.Property.ValueChangeEvent; | |||||
import com.vaadin.data.Property.ValueChangeListener; | |||||
import com.vaadin.data.util.ObjectProperty; | |||||
import com.vaadin.navigator.Navigator; | |||||
import com.vaadin.navigator.View; | |||||
import com.vaadin.navigator.ViewChangeListener; | |||||
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; | |||||
import com.vaadin.ui.Button; | |||||
import com.vaadin.ui.Button.ClickEvent; | |||||
import com.vaadin.ui.DateField; | |||||
import com.vaadin.ui.InlineDateField; | |||||
import com.vaadin.ui.Layout; | |||||
import com.vaadin.ui.Notification; | |||||
import com.vaadin.ui.Notification.Type; | |||||
import com.vaadin.ui.Panel; | |||||
import com.vaadin.ui.VerticalLayout; | |||||
import com.vaadin.ui.themes.Reindeer; | |||||
public class SettingsView extends Panel implements View { | |||||
public static String NAME = "settings"; | |||||
Navigator navigator; | |||||
DateField date; | |||||
Button apply; | |||||
Button cancel; | |||||
String pendingViewAndParameters = null; | |||||
public SettingsView(final Navigator navigator) { | |||||
this.navigator = navigator; | |||||
Layout layout = new VerticalLayout(); | |||||
date = new InlineDateField("Birth date"); | |||||
date.setImmediate(true); | |||||
layout.addComponent(date); | |||||
// pretend we have a datasource: | |||||
date.setPropertyDataSource(new ObjectProperty<Date>(new Date())); | |||||
date.setBuffered(true); | |||||
// show buttons when date is changed | |||||
date.addValueChangeListener(new ValueChangeListener() { | |||||
public void valueChange(ValueChangeEvent event) { | |||||
hideOrShowButtons(); | |||||
pendingViewAndParameters = null; | |||||
} | |||||
}); | |||||
// commit the TextField changes when "Save" is clicked | |||||
apply = new Button("Apply", new Button.ClickListener() { | |||||
public void buttonClick(ClickEvent event) { | |||||
date.commit(); | |||||
hideOrShowButtons(); | |||||
processPendingView(); | |||||
} | |||||
}); | |||||
layout.addComponent(apply); | |||||
// Discard the TextField changes when "Cancel" is clicked | |||||
cancel = new Button("Cancel", new Button.ClickListener() { | |||||
public void buttonClick(ClickEvent event) { | |||||
date.discard(); | |||||
hideOrShowButtons(); | |||||
processPendingView(); | |||||
} | |||||
}); | |||||
cancel.setStyleName(Reindeer.BUTTON_LINK); | |||||
layout.addComponent(cancel); | |||||
// attach a listener so that we'll get asked isViewChangeAllowed? | |||||
navigator.addViewChangeListener(new ViewChangeListener() { | |||||
public boolean beforeViewChange(ViewChangeEvent event) { | |||||
if (event.getOldView() == SettingsView.this | |||||
&& date.isModified()) { | |||||
// save the View where the user intended to go | |||||
pendingViewAndParameters = event.getViewName(); | |||||
if (event.getParameters() != null) { | |||||
pendingViewAndParameters += "/"; | |||||
pendingViewAndParameters += event | |||||
.getParameters(); | |||||
} | |||||
// Prompt the user to save or cancel if the name is changed | |||||
Notification.show("Please apply or cancel your changes", | |||||
Type.WARNING_MESSAGE); | |||||
return false; | |||||
} else { | |||||
return true; | |||||
} | |||||
} | |||||
public void afterViewChange(ViewChangeEvent event) { | |||||
pendingViewAndParameters = null; | |||||
} | |||||
}); | |||||
setContent(layout); | |||||
} | |||||
// Hide or show buttons depending on whether date is modified or not | |||||
private void hideOrShowButtons() { | |||||
apply.setVisible(date.isModified()); | |||||
cancel.setVisible(date.isModified()); | |||||
} | |||||
// if there is a pending view change, do it now | |||||
private void processPendingView() { | |||||
if (pendingViewAndParameters != null) { | |||||
navigator.navigateTo(pendingViewAndParameters); | |||||
pendingViewAndParameters = null; | |||||
} | |||||
} | |||||
@Override | |||||
public void enter(ViewChangeEvent event) { | |||||
hideOrShowButtons(); | |||||
} | |||||
} | |||||
.... | |||||
First we set up a `DateField` with buffering and a (dummy) datasource to | |||||
make this work more as a real application would. With buffering on, the | |||||
value (date in this case) can be changed, but it will not be written to | |||||
the datasource before we `commit()`, which is what the Save -button does. | |||||
The Cancel -button does `discard()` on the DateField, which returns the | |||||
field to its unmodified state. | |||||
The buttons do not need to be shown if nothing has changed, so we add a | |||||
`ValueChangeListener` to the `DateField` for that purpose. | |||||
But the main thing that we're trying to demonstrate here happens in the | |||||
`ViewChangeListener` that we attach to the `Navigator`. There, if we're | |||||
about to change _away_ from our settings _and_ the date is changed but | |||||
_not_ saved, we'll make note of where the user wanted to go, but cancel | |||||
that navigation and prompt the user to save or cancel the changes. | |||||
When the user saves or cancels changes, we also check if the user | |||||
previously tried to navigate away form the page, and sends him on his | |||||
way if that is the case. | |||||
That is basically all there is to this. You'll notice we try to | |||||
carefully clear or set the 'pending view' and hide/show the buttons at | |||||
the right places to make the user happy, other than that this is pretty | |||||
straightforward. |
--- | |||||
title: Widget Styling Using Only CSS | |||||
order: 52 | |||||
layout: page | |||||
--- | |||||
[[widget-styling-using-only-css]] | |||||
Widget styling using only CSS | |||||
----------------------------- | |||||
The preferred way of styling your widget is to only use static CSS | |||||
included in the theme's styles.css file. For information on how to | |||||
create custom themes, please refer to | |||||
https://vaadin.com/book/-/page/themes.creating.html. Your component can | |||||
be styled using the CSS class names that are defined to the widget using | |||||
`setStyleName`. | |||||
Normal styling of components works in the same way as any styling using | |||||
CSS, but there are some special features to pay attention to when Vaadin | |||||
7 is used. | |||||
[[some-sizing-theory]] | |||||
Some sizing theory | |||||
~~~~~~~~~~~~~~~~~~ | |||||
All Vaadin 7 components get the CSS class v-widget which sets the | |||||
box-sizing to border-box. This causes borders and paddings to be | |||||
considered when the browser calculates the component's size. This means | |||||
that e.g. a component with padding: 5px and width: 100% inside a 200px | |||||
wide slot will fill the slot without any overflow because the inner | |||||
width of the component will be calculated to 190px by the browser. | |||||
The Vaadin 7 server side API allows setting the size of the component in | |||||
three different ways: | |||||
* Undefined size, set e.g. using `setWidth(null)`, means that the | |||||
component's size should have it's default size that might vary depending | |||||
on its content. | |||||
* Relative size, set e.g. using `setWidth("100%")` means that the | |||||
component's size is determined by the size and settings of the | |||||
component's parent. | |||||
* Fixed size, set e.g. using `setWidth("250px")` or `setWidth("10em")` means | |||||
that the component's size is fixed, so that the parent doesn't affect | |||||
the size and neither does the component's content. | |||||
The three different ways of setting the size means that a component | |||||
should both support having its own natural size and filling the | |||||
allocated space depending on how the size is set. This usually means | |||||
that the main area of the component should adjust based on the settings. | |||||
[[a-simple-sample]] | |||||
A simple sample | |||||
~~~~~~~~~~~~~~~ | |||||
Consider e.g. a simple date picker component with a text field where a | |||||
date can be entered and where the currently selected is displayed and a | |||||
button that is used to open a calendar view where a date can be picked | |||||
using the mouse. | |||||
[source,java] | |||||
.... | |||||
public class MyPickerWidget extends ComplexPanel { | |||||
public static final String CLASSNAME = "mypicker"; | |||||
private final TextBox textBox = new TextBox(); | |||||
private final PushButton button = new PushButton("..."); | |||||
public MyPickerWidget() { | |||||
setElement(Document.get().createDivElement()); | |||||
setStylePrimaryName(CLASSNAME); | |||||
textBox.setStylePrimaryName(CLASSNAME + "-field"); | |||||
button.setStylePrimaryName(CLASSNAME + "-button"); | |||||
add(textBox, getElement()); | |||||
add(button, getElement()); | |||||
button.addClickHandler(new ClickHandler() { | |||||
public void onClick(ClickEvent event) { | |||||
Window.alert("Calendar picker not yet supported!"); | |||||
} | |||||
}); | |||||
} | |||||
} | |||||
.... | |||||
We then add this basic styling to the theme CSS file | |||||
[source,scss] | |||||
.... | |||||
.mypicker { | |||||
white-space: nowrap; | |||||
} | |||||
.mypicker-button { | |||||
display: inline-block; | |||||
border: 1px solid black; | |||||
padding: 3px; | |||||
width: 15px; | |||||
text-align: center; | |||||
} | |||||
.... | |||||
`display: inline-block` makes the button continue on the same line as the | |||||
text field, placing it to the right of the field. We also add | |||||
`white-space: nowrap` to the main div element to ensure the button is not | |||||
wrapped to the next row. Finally, there is some padding and a border to | |||||
make the button look more like a real button. | |||||
[[using-available-space]] | |||||
Using available space | |||||
^^^^^^^^^^^^^^^^^^^^^ | |||||
This simple layout works well as long as the component's has it's | |||||
default undefined width. Changing the width from the server does however | |||||
not have any visible effect because only the size of the main div is | |||||
changed. If the component is made smaller, the contents goes beyond the | |||||
size of the element (this can be verified by adding `overflow: hidden;` to | |||||
`.mypicker`) and if it gets larger the extra space is just left as empty | |||||
space to the right of the button. | |||||
The first step towards making the size adjust is to make the text field | |||||
adjust to the main div element's width whenever the width is something | |||||
else then than undefined. In these situations, Vaadin 7 adds a | |||||
`v-has-width` class to the component's main element (`v-has-height` added | |||||
when the height is not undefined). | |||||
[source,scss] | |||||
.... | |||||
.mypicker.v-has-width > .mypicker-field { | |||||
width: 100%; | |||||
} | |||||
.... | |||||
With this additional CSS, the text field directly inside a picker that | |||||
has a defined width gets full width. | |||||
[[making-room-for-the-button-again]] | |||||
Making room for the button again | |||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |||||
We're however not done yet. Setting the width of the text field to 100% | |||||
makes it as wide as the main div is, which means that the button goes | |||||
outside the main div. This can be verified using the DOM inspector in | |||||
your browser or by setting `overflow: hidden` to the style for `mypicker`. | |||||
To reserve space for the button, we can add some padding to the right | |||||
edge of the main div element. This does however cause the padding space | |||||
to remain unused if the component size is undefined. To compensate for | |||||
this, negative margin can be added to the right edge of the button, | |||||
effectively reducing its occupied size to 0px. | |||||
Finally, we need to use `box-sizing: border-box` to make the field's | |||||
borders and paddings be included in the 100% width. | |||||
The full CSS for the sample component: | |||||
[source,scss] | |||||
.... | |||||
.mypicker { | |||||
white-space: nowrap; | |||||
padding-right: 23px; | |||||
} | |||||
.mypicker-button { | |||||
display: inline-block; | |||||
border: 1px solid black; | |||||
padding: 3px; | |||||
width: 15px; | |||||
text-align: center; | |||||
margin-right: -23px; | |||||
} | |||||
.mypicker.v-has-width > .mypicker-field { | |||||
width: 100%; | |||||
} | |||||
.mypicker-field { | |||||
-moz-box-sizing: border-box; | |||||
-webkit-boz-sizing: border-box; | |||||
box-sizing: border-box; | |||||
} | |||||
.... |
--- | |||||
-- | |||||
title: Contents | title: Contents | ||||
order: 1 | order: 1 | ||||
layout: page | layout: page | ||||
--- | |||||
-- | |||||
= Community articles for Vaadin 7 | = Community articles for Vaadin 7 | ||||
[discrete] | [discrete] | ||||
== Articles | == Articles | ||||
- <<LazyQueryContainer#lazy-query-container,"Lazy query container">> | - <<LazyQueryContainer#lazy-query-container,"Lazy query container">> | ||||
- <<UsingGridWithInlineData#using-grid-with-inline-data,"Using Grid with inline data">> | - <<UsingGridWithInlineData#using-grid-with-inline-data,"Using Grid with inline data">> | ||||
- <<MigratingFromVaadin6ToVaadin7#migrating-from-vaadin-6-to-vaadin-7,"Migrating from Vaadin 6 to Vaadin 7">> | - <<MigratingFromVaadin6ToVaadin7#migrating-from-vaadin-6-to-vaadin-7,"Migrating from Vaadin 6 to Vaadin 7">> | ||||
- <<MigratingFromVaadin7%2E0ToVaadin7%2E1#migrating-from-vaadin-7.0-to-vaadin-7.1,"Migrating from Vaadin 7.0 to Vaadin 7.1">> | - <<MigratingFromVaadin7%2E0ToVaadin7%2E1#migrating-from-vaadin-7.0-to-vaadin-7.1,"Migrating from Vaadin 7.0 to Vaadin 7.1">> | ||||
- <<UsingParametersWithViews#using-parameters-with-views,"Using parameters with views">> | |||||
- <<SendingEventsFromTheClientToTheServerUsingRPC#sending-events-from-the-client-to-the-server-using-RPC,"Sending events from the client to the server using RPC">> | |||||
- <<IntegratingAJavaScriptComponent#integrating-a-javascript-component,"Integrating a JavaScript component">> | |||||
- <<IntegratingAJavaScriptLibraryAsAnExtension#integrating-a-javascript-library-as-an-extension,"Integrating a JavaScript library as an extension">> | |||||
- <<UsingAJavaScriptLibraryOrAStyleSheetInAnAddOn#using-a-javascript-library-or-a-style-sheet-in-an-addon,"Using a JavaScript library or a style sheet in an add-on">> | |||||
- <<ExposingServerSideAPIToJavaScript#exposing-server-side-api-to-javascript,"Exposing Server Side API to JavaScript">> | |||||
- <<UsingRPCFromJavaScript#using-rpc-from-javascript,"Using RPC from JavaScript">> | |||||
- <<CustomizingTheStartupPageInAnApplication#customizing-the-startup-page-in-an-application,"Customizing the startup page in an application">> | |||||
- <<OptimizingTheWidgetSet#optimizing-the-widget-set,"Optimizing the widget set">> | |||||
- <<UsingBeanValidationToValidateInput#using-bean-validation-to-validate-input,"Using Bean Validation to validate input">> | |||||
- <<AccessControlForViews#access-control-for-views,"Access control for views">> | |||||
- <<CustomizingComponentThemeWithSass#customizing-component-theme-with-sass,"Customizing component theme with Sass">> | |||||
- <<CreatingASimpleComponent#creating-a-simple-component,"Creating a simple component">> | |||||
- <<CreatingASimpleComponentContainer#creating-a-simple-component-container,"Creating a simple component container">> | |||||
- <<CreatingAUIExtension#creating-a-ui-extension,"Creating a UI extension">> | |||||
- <<CreatingAComponentExtension#creating-a-component-extension,"Creating a component extension">> | |||||
- <<WidgetStylingUsingOnlyCSS#widget-styling-using-only-css,"Widget styling using only CSS">> | |||||
- <<CreatingAThemeUsingSass#creating-a-theme-using-sass,"Creating a theme using Sass">> | |||||
- <<ViewChangeConfirmations#view-change-confirmations,"View change confirmations">> | |||||
- <<CreatingABookmarkableApplicationWithBackButtonSupport#creating-a-bookmarkable-application-with-back-button-support,"Creating a bookmarkable application with back button support">> | |||||
- <<UsingRPCToSendEventsToTheClient#using-rpc-to-send-events-to-the-client,"Using RPC to send events to the client">> | |||||