@@ -0,0 +1,206 @@ | |||
--- | |||
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; | |||
} | |||
} | |||
.... |
@@ -9,8 +9,8 @@ layout: page | |||
To try out how the widths of Grid columns work in different situations, | |||
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 | |||
of data and allocate column widths based on that. If there's room to |
@@ -0,0 +1,146 @@ | |||
--- | |||
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. |
@@ -0,0 +1,98 @@ | |||
--- | |||
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); | |||
.... |
@@ -0,0 +1,146 @@ | |||
--- | |||
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. |
@@ -0,0 +1,249 @@ | |||
--- | |||
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); | |||
} | |||
} | |||
.... |
@@ -0,0 +1,201 @@ | |||
--- | |||
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! |
@@ -0,0 +1,178 @@ | |||
--- | |||
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); | |||
} | |||
.... |
@@ -0,0 +1,191 @@ | |||
--- | |||
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; | |||
} | |||
} | |||
.... |
@@ -0,0 +1,120 @@ | |||
--- | |||
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); | |||
} | |||
}); | |||
} | |||
} | |||
.... |
@@ -0,0 +1,140 @@ | |||
--- | |||
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);})();"))); | |||
} | |||
} | |||
.... |
@@ -114,8 +114,8 @@ Full example | |||
^^^^^^^^^^^^ | |||
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] | |||
.... |
@@ -0,0 +1,116 @@ | |||
--- | |||
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. | |||
==== |
@@ -0,0 +1,96 @@ | |||
--- | |||
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. | |||
==== |
@@ -8,8 +8,8 @@ layout: page | |||
= Migrating from Vaadin 6 to Vaadin 7 | |||
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 | |||
@@ -71,7 +71,7 @@ public class V6tm1Application extends Application { | |||
Label label = new Label("Hello Vaadin!"); | |||
mainWindow.addComponent(label); | |||
setMainWindow(mainWindow); | |||
setTheme(“mytheme”); | |||
setTheme("mytheme"); | |||
} | |||
} | |||
.... | |||
@@ -180,9 +180,9 @@ In Vaadin 6, Window, Panel and some other components had a *default | |||
layout* and addComponent() etc. As this often caused confusion and | |||
caused layout problems when unaware of the implicit layout or forgetting | |||
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 | |||
old application, you can reproduce the *old structure* simply by setting | |||
@@ -213,8 +213,9 @@ of synchronizing to the Application instance - see the javadoc for | |||
To customize the creation of UIs - for instance to create different UIs | |||
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 | |||
@@ -231,21 +232,24 @@ data binding works mostly as is, version 7 brings something better: | |||
* *FieldGroup* supporting *automated data binding*, whether for a hand-designed | |||
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 | |||
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 | |||
conversion) - see e.g. | |||
link:UsingBeanValidationToValidateInput.asciidoc[bean validation example] | |||
<<UsingBeanValidationToValidateInput#using-bean-validation-to-validate-input,bean validation example>> | |||
* and more | |||
@@ -305,8 +309,7 @@ optional additional optional parameters before the module name. | |||
If you have optimized your widgetset to limit what components to load | |||
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 | |||
Optimizer add-on]. | |||
@@ -332,18 +335,18 @@ fully migrating your themes to the SCSS format with a theme name | |||
selector. | |||
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 | |||
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 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 | |||
changes to themes for layouts. See also the section on layouts below. | |||
@@ -359,14 +362,12 @@ navigate to them. | |||
The best way to get acquainted with the new navigation features is to | |||
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 | |||
the user to a suitable page. | |||
@@ -380,8 +381,8 @@ As ApplicationServlet moved to history and is replaced by | |||
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 | |||
communication between client and server) probably need more extensive | |||
rework | |||
@@ -413,20 +414,21 @@ server side component Label, but the communication part has been split | |||
off into LabelConnector. The annotations linking the client side and the | |||
server side have also changed, now the LabelConnector has an *@Connect* | |||
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 | |||
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 | |||
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, | |||
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 | |||
still there for a while to ease migration, but it is recommended to | |||
@@ -436,15 +438,16 @@ in much cleaner code. Using the old mechanisms requires implementing | |||
There are also new features such as support for *Extensions* (components | |||
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 | |||
and RPC can also be used from JavaScript, and there are other techniques | |||
for client-server communication. | |||
@@ -550,8 +553,8 @@ side component containers, but a few can also affect other developers. | |||
Among the changes affecting others than layout developers, *CssLayout* | |||
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 | |||
client, which might require changes to themes. The interface | |||
*MarginHandler* is now only implemented by layouts that actually support | |||
@@ -565,10 +568,10 @@ of *ComponentContainer*. | |||
For those implementing new component containers or layouts, see the | |||
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 |
@@ -13,9 +13,9 @@ This guide describes how to migrate from earlier versions to Vaadin 7.1. | |||
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 |
@@ -0,0 +1,193 @@ | |||
--- | |||
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. |
@@ -0,0 +1,145 @@ | |||
--- | |||
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. |
@@ -18,8 +18,8 @@ expanded, and then you need to hook up the events for actually expanding | |||
a row. | |||
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 |
@@ -8,16 +8,17 @@ layout: page | |||
= Simplified RPC using JavaScript | |||
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 | |||
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 | |||
interfaces containing multiple methods. We will invoke a server-side |
@@ -0,0 +1,61 @@ | |||
--- | |||
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. | |||
==== |
@@ -0,0 +1,59 @@ | |||
--- | |||
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 |
@@ -8,7 +8,8 @@ layout: page | |||
= Using Grid with inline data | |||
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 | |||
using a Container. | |||
@@ -0,0 +1,118 @@ | |||
--- | |||
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. |
@@ -0,0 +1,114 @@ | |||
--- | |||
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. |
@@ -0,0 +1,154 @@ | |||
--- | |||
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. |
@@ -12,7 +12,7 @@ user belongs to in a Hierarchical Tree. I used Vaadin's tree and | |||
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). | |||
See link:img/DmoOrgTreeUI.java[DmoOrgTreeUI.java] for full source code. | |||
See <<img/DmoOrgTreeUI.java,DmoOrgTreeUI.java>> for full source code. | |||
[source,java] | |||
.... |
@@ -0,0 +1,226 @@ | |||
--- | |||
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. |
@@ -0,0 +1,178 @@ | |||
--- | |||
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; | |||
} | |||
.... |
@@ -1,11 +1,11 @@ | |||
--- | |||
-- | |||
title: Contents | |||
order: 1 | |||
layout: page | |||
--- | |||
-- | |||
= Community articles for Vaadin 7 | |||
[discrete] | |||
== Articles | |||
- <<LazyQueryContainer#lazy-query-container,"Lazy query container">> | |||
@@ -42,3 +42,26 @@ layout: page | |||
- <<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">> | |||
- <<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">> | |||