1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
---
title: Component and UI Extensions
order: 7
layout: page
---
[[gwt.extension]]
= Component and UI Extensions
Adding features to existing components by extending them by inheritance creates
a problem when you want to combine such features. For example, one add-on could
add spell-check to a [classname]#TextField#, while another could add client-side
validation. Combining such add-on features would be difficult if not impossible.
You might also want to add a feature to several or even to all components, but
extending all of them by inheritance is not really an option. Vaadin includes a
component plug-in mechanism for these purposes. Such plug-ins are simply called
__extensions__.
Also a UI can be extended in a similar fashion. In fact, some Vaadin features
such as the JavaScript execution are UI extensions.
Implementing an extension requires defining a server-side extension class and a
client-side connector. An extension can have a shared state with the connector
and use RPC, just like a component could.
[[gwt.extension.server-side]]
== Server-Side Extension API
The server-side API for an extension consists of class that extends (in the Java
sense) the [classname]#AbstractExtension# class. It typically has an
__extend()__ method, a constructor, or a static helper method that takes the
extended component or UI as a parameter and passes it to __super.extend()__.
For example, let us have a trivial example with an extension that takes no
special parameters, and illustrates the three alternative APIs:
----
public class CapsLockWarning extends AbstractExtension {
// You could pass it in the constructor
public CapsLockWarning(PasswordField field) {
super.extend(field);
}
// Or in an extend() method
public void extend(PasswordField field) {
super.extend(field);
}
// Or with a static helper
public static addTo(PasswordField field) {
new CapsLockWarning().extend(field);
}
}
----
The extension could then be added to a component as follows:
----
PasswordField password = new PasswordField("Give it");
// Use the constructor
new CapsLockWarning(password);
// ... or with the extend() method
new CapsLockWarning().extend(password);
// ... or with the static helper
CapsLockWarning.addTo(password);
layout.addComponent(password);
----
Adding a feature in such a "reverse" way is a bit unusual in the Vaadin API, but
allows type safety for extensions, as the method can limit the target type to
which the extension can be applied, and whether it is a regular component or a
UI.
[[gwt.extension.connector]]
== Extension Connectors
An extension does not have a corresponding widget on the client-side, but only
an extension connector that extends the [classname]#AbstractExtensionConnector#
class. The server-side extension class is specified with a
[literal]#++@Connect++# annotation, just like in component connectors.
An extension connector needs to implement the [methodname]#extend()# method,
which allows hooking to the extended component. The normal extension mechanism
is to modify the extended component as needed and add event handlers to it to
handle user interaction. An extension connector can share a state with the
server-side extension as well as make RPC calls, just like with components.
In the following example, we implement a "Caps Lock warning" extension. It
listens for changes in Caps Lock state and displays a floating warning element
over the extended component if the Caps Lock is on.
----
@Connect(CapsLockWarning.class)
public class CapsLockWarningConnector
extends AbstractExtensionConnector {
@Override
protected void extend(ServerConnector target) {
// Get the extended widget
final Widget pw =
((ComponentConnector) target).getWidget();
// Preparations for the added feature
final VOverlay warning = new VOverlay();
warning.setOwner(pw);
warning.add(new HTML("Caps Lock is enabled!"));
// Add an event handler
pw.addDomHandler(new KeyPressHandler() {
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());
}
}
----
The [methodname]#extend()# method gets the connector of the extended component
as the parameter, in the above example a [classname]#PasswordFieldConnector#. It
can access the widget with the [methodname]#getWidget()#.
An extension connector needs to be included in a widget set. The class must
therefore be defined under the [filename]#client# package of a widget set, just
like with component connectors.
|