summaryrefslogtreecommitdiffstats
path: root/documentation/gwt/gwt-extension.asciidoc
diff options
context:
space:
mode:
Diffstat (limited to 'documentation/gwt/gwt-extension.asciidoc')
-rw-r--r--documentation/gwt/gwt-extension.asciidoc145
1 files changed, 145 insertions, 0 deletions
diff --git a/documentation/gwt/gwt-extension.asciidoc b/documentation/gwt/gwt-extension.asciidoc
new file mode 100644
index 0000000000..efaece40ab
--- /dev/null
+++ b/documentation/gwt/gwt-extension.asciidoc
@@ -0,0 +1,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.
+
+
+
+