You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CreatingAUIExtension.asciidoc 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. ---
  2. title: Creating A UI Extension
  3. order: 50
  4. layout: page
  5. ---
  6. [[creating-a-ui-extension]]
  7. Creating a UI extension
  8. -----------------------
  9. An *Extension* is an entity that is not a full-fledged UI component, but
  10. is instead used to enhance or extend the functionality of an existing
  11. component (or connector, more generally.) Unlike components, extensions
  12. cannot be detached and reattached once they are attached to their
  13. target.
  14. Extensions usually consist of a pair of `Connector`{empty}s like components do.
  15. Hence, they can use the regular shared state and RPC mechanisms to
  16. communicate between the client and the server. Extensions may or may not
  17. have a UI. They can create and display widgets on the client side, but
  18. are not part of the regular layout hierarchy.
  19. We will rewrite the
  20. https://vaadin.com/directory/component/refresher[Refresher] add-on as an
  21. extension. The Refresher causes the client to "ping" the server at
  22. regular intervals, allowing the server to keep the client up-to-date if
  23. the application state is changed eg. by a background thread (because of
  24. the way Vaadin works, the server cannot itself initiate communication.)
  25. We start by writing the barebones server-side class for our extension:
  26. [source,java]
  27. ....
  28. public class Refresher extends AbstractExtension {
  29. public Refresher(UI ui) {
  30. extend(target);
  31. }
  32. }
  33. ....
  34. Two things to note:
  35. * If we were writing a component, we would probably want to inherit from
  36. `AbstractComponent`. Here, we inherit from `AbstractExtension` instead.
  37. * The connector that should be extended is passed to the constructor,
  38. which then uses the protected `extend(Connector)` method to attach
  39. itself to the target connector. In this case it does not make much sense
  40. attached to individual components, so the constructor only accepts `UI`.
  41. Next, the Refresher needs an RPC interface to ping the server and a
  42. shared state to keep track of the interval. These are rather trivial:
  43. [source,java]
  44. ....
  45. public interface RefresherRpc extends ServerRpc {
  46. public void refresh();
  47. }
  48. ....
  49. [source,java]
  50. ....
  51. public class RefresherState extends SharedState {
  52. public int interval;
  53. }
  54. ....
  55. The client-side connector is just like a component connector except that
  56. we inherit from `AbstractExtensionConnector`, not
  57. `AbstractComponentConnector`. We do not write a client-side widget at
  58. all, because the Refresher does not have a UI.
  59. We create a `Timer` instance that calls the `refresh` RPC method when
  60. run. In `onStateChange()`, we know that either the interval, enabled
  61. state, or both have changed, so we always cancel a possible
  62. currently-running timer and schedule a new one if we're enabled. We also
  63. remember to cancel the timer when the extension is detached.
  64. [source,java]
  65. ....
  66. @Connect(Refresher.class)
  67. public class RefresherConnector extends AbstractExtensionConnector {
  68. private Timer timer = new Timer() {
  69. @Override
  70. public void run() {
  71. getRpcProxy(RefresherRpc.class).refresh();
  72. }
  73. };
  74. @Override
  75. public void onStateChanged(StateChangeEvent event) {
  76. super.onStateChanged(event);
  77. timer.cancel();
  78. if (isEnabled()) {
  79. timer.scheduleRepeating(getState().interval);
  80. }
  81. }
  82. @Override
  83. public void onUnregister() {
  84. timer.cancel();
  85. }
  86. @Override
  87. protected void extend(ServerConnector target) {
  88. // Nothing for refresher to do here as it does not need to access the
  89. // connector it extends
  90. }
  91. @Override
  92. public RefresherState getState() {
  93. return (RefresherState) super.getState();
  94. }
  95. }
  96. ....
  97. Finally, we add an event listener interface and some accessor methods to
  98. `Refresher`. There is nothing extension-specific in the following code:
  99. [source,java]
  100. ....
  101. public interface RefreshListener {
  102. static Method METHOD = ReflectTools.findMethod(RefreshListener.class,
  103. "refresh", RefreshEvent.class);
  104. public void refresh(RefreshEvent refreshEvent);
  105. }
  106. public class RefreshEvent extends EventObject {
  107. public RefreshEvent(Refresher refresher) {
  108. super(refresher);
  109. }
  110. public Refresher getRefresher() {
  111. return (Refresher) getSource();
  112. }
  113. }
  114. public Refresher(UI ui) {
  115. registerRpc(new RefresherRpc() {
  116. @Override
  117. public void refresh() {
  118. fireEvent(new RefreshEvent(Refresher.this));
  119. }
  120. });
  121. extend(ui);
  122. }
  123. @Override
  124. public RefresherState getState() {
  125. return (RefresherState) super.getState();
  126. }
  127. public void setInterval(int millis) {
  128. getState().interval = millis;
  129. }
  130. public int getInterval() {
  131. return getState().interval;
  132. }
  133. public void setEnabled(boolean enabled) {
  134. getState().enabled = enabled;
  135. }
  136. public boolean isEnabled() {
  137. return getState().enabled;
  138. }
  139. public void addRefreshListener(RefreshListener listener) {
  140. super.addListener(RefreshEvent.class, listener, RefreshListener.METHOD);
  141. }
  142. public void removeRefreshListener(RefreshListener listener) {
  143. super.removeListener(RefreshEvent.class, listener,
  144. RefreshListener.METHOD);
  145. }
  146. ....