diff options
Diffstat (limited to 'documentation/gwt/gwt-extension.asciidoc')
-rw-r--r-- | documentation/gwt/gwt-extension.asciidoc | 145 |
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. + + + + |