123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- ---
- title: Creating A UI Extension
- order: 73
- 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);
- }
- ....
|