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.

CreatingASimpleComponentContainer.asciidoc 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. ---
  2. title: Creating A Simple Component Container
  3. order: 70
  4. layout: page
  5. ---
  6. [[creating-a-simple-component-container]]
  7. = Creating a simple component container
  8. Components in Vaadin can be roughly split into two groups, `Component`{empty}s
  9. and `ComponentContainer`{empty}s. ComponentContainers are Components in
  10. themselves which can also contain other components. If you are about to
  11. implement a component that contains other components, then you'll get a
  12. headstart by extending Vaadin's `ComponentContainer`. The biggest feature
  13. is in transferring the list of server side components from your component
  14. to the client. Here's how you do it.
  15. [[server-side]]
  16. Server Side
  17. ^^^^^^^^^^^
  18. To start of we implement our server side component. For this we extend
  19. the ready made abstract implementation `AbstractComponentContainer`. This
  20. requires us to implement `addComponent(Component)`,
  21. `removeComponent(Component)`, `replaceComponent(Component, Component)`,
  22. `getComponentCount` and `getComponentIterator()`.
  23. [source,java]
  24. ....
  25. package com.example.widgetcontainer;
  26. import java.util.ArrayList;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import com.vaadin.ui.AbstractComponentContainer;
  30. import com.vaadin.ui.Component;
  31. public class WidgetContainer extends AbstractComponentContainer {
  32. List<Component> children = new ArrayList<Component>();
  33. @Override
  34. public void addComponent(Component c) {
  35. children.add(c);
  36. super.addComponent(c);
  37. markAsDirty();
  38. }
  39. @Override
  40. public void removeComponent(Component c) {
  41. children.remove(c);
  42. super.removeComponent(c);
  43. markAsDirty();
  44. }
  45. public void replaceComponent(Component oldComponent, Component newComponent) {
  46. int index = children.indexOf(oldComponent);
  47. if (index != -1) {
  48. children.remove(index);
  49. children.add(index, newComponent);
  50. fireComponentDetachEvent(oldComponent);
  51. fireComponentAttachEvent(newComponent);
  52. markAsDirty();
  53. }
  54. }
  55. public int getComponentCount() {
  56. return children.size();
  57. }
  58. public Iterator<Component> iterator() {
  59. return children.iterator();
  60. }
  61. }
  62. ....
  63. Add, remove and replace are quite straight forward. In the class we
  64. upkeep a list of children internally, and these three methods modify
  65. them. Add and remove have ready made methods in the super class for
  66. notifying all event handlers that the children have changed and because
  67. of that we should make calls to the super methods after we have updated
  68. the list. In `replaceComponent` we have to call
  69. `fireComponentDetachEvent(Component)` and
  70. `fireComponentAttachEvent(Component)` to manually trigger these events. In
  71. all three methods we should also call `markAsDirty` as a last step to
  72. notify the client side that the children have changed.
  73. The methods `getComponentCount()` and `iterator()` takes care of providing
  74. the required information that we need to the client side. Here they are
  75. simple delegate methods to the List's `size()` and `iterator()`.
  76. [[client-side]]
  77. Client Side
  78. ^^^^^^^^^^^
  79. Next up, we want to set up a standard GWT widget which will be our
  80. component container's client side widget. GWT in itself has a bunch of
  81. component containers in it. In GWT, these are called Panels. For this
  82. case I will start with a `VerticalPanel`. It is roughly the same as
  83. `VerticalLayout` in Vaadin. Down the road you want to edit this file to
  84. add features or even extend Widget to create a complete custom widget.
  85. For now extending `VerticalPanel` is enough and we'll use that as-is.
  86. [source,java]
  87. ....
  88. package com.example.widgetcontainer.client.ui;
  89. import com.google.gwt.user.client.ui.VerticalPanel;
  90. public class VWidgetContainer extends VerticalPanel {
  91. public static final String CLASSNAME = "v-widgetcontainer";
  92. public VWidgetContainer() {
  93. setStyleName(CLASSNAME);
  94. }
  95. }
  96. ....
  97. [[connector]]
  98. Connector
  99. ^^^^^^^^^
  100. Your widget's Connector will transfer the components from the server
  101. side as child widgets to our widget. The connector will feed the
  102. children to the panel trough it's standard API, namely `add(Widget)`,
  103. `remove(Widget)` and `clear();`
  104. Instead of going the standard route of extending
  105. `AbstractComponentConnector` as your connector, here we can take use of
  106. Vaadin's internal features and extend
  107. `AbstractComponentContainerConnector`. Additionally to implementing the
  108. `getWidget()` -method from `AbstractComponentConnector`, we also have to
  109. supply the class with an implementation to a method called
  110. `updateCaption(ComponentConnector)`. This method is there if we want the
  111. container to take care of the captions for all the components. We don't
  112. need to take care of these captions in this example so we can leave the
  113. implementation empty.
  114. The real benefit of extending `AbstractComponentContainerConnector` is
  115. that we can now extend a method called
  116. `onConnectorHierarchyChange(ConnectorHierarchyChangeEvent)`. This method
  117. will be called every time that the server side calls `markAsDirty()` if
  118. the component hierarchy has been changed. From within it, we can call on
  119. `getChildComponents` to get a list of all the child components, and
  120. populate our widget with those.
  121. [source,java]
  122. ....
  123. package com.example.widgetcontainer.client.ui;
  124. import java.util.List;
  125. import com.google.gwt.core.client.GWT;
  126. import com.google.gwt.user.client.ui.Widget;
  127. import com.example.widgetcontainer.WidgetContainer;
  128. import com.vaadin.client.ComponentConnector;
  129. import com.vaadin.client.ConnectorHierarchyChangeEvent;
  130. import com.vaadin.client.ui.AbstractComponentContainerConnector;
  131. import com.vaadin.client.ui.Connect;
  132. @Connect(WidgetContainer.class)
  133. public class WidgetContainerConnector extends
  134. AbstractComponentContainerConnector {
  135. @Override
  136. public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
  137. List<ComponentConnector> children = getChildComponents();
  138. VWidgetContainer widget = getWidget();
  139. widget.clear();
  140. for (ComponentConnector connector : children) {
  141. widget.add(connector.getWidget());
  142. }
  143. }
  144. @Override
  145. public VWidgetContainer getWidget() {
  146. return (VWidgetContainer) super.getWidget();
  147. }
  148. public void updateCaption(ComponentConnector connector) {
  149. }
  150. }
  151. ....
  152. This implementation removes all the component's in the widget and adds
  153. all that are returned from `getChildComponents`. An obvious optimization
  154. to these is to compare what is already in the widget and only
  155. add/remove/move those widgets that have changed.
  156. [[example-usage]]
  157. Example Usage
  158. ^^^^^^^^^^^^^
  159. Nothing left but to use the component! Compile the widgetset and check
  160. that the widgetset is in use in your web.xml. Here is a little
  161. stand-alone application that uses this component:
  162. [source,java]
  163. ....
  164. package com.example.widgetcontainer;
  165. import java.util.Random;
  166. import com.vaadin.terminal.WrappedRequest;
  167. import com.vaadin.ui.Button;
  168. import com.vaadin.ui.Button.ClickEvent;
  169. import com.vaadin.ui.Button.ClickListener;
  170. import com.vaadin.ui.CheckBox;
  171. import com.vaadin.ui.Component;
  172. import com.vaadin.ui.Label;
  173. import com.vaadin.ui.UI;
  174. public class WidgetcontainerUI extends UI {
  175. @Override
  176. public void init(VaadinRequest request) {
  177. VerticalLayout layout = new VerticalLayout();
  178. layout.setMargin(true);
  179. setContent(layout);
  180. Label label = new Label("Hello Vaadin user");
  181. layout.addComponent(label);
  182. final WidgetContainer widgetContainer = new WidgetContainer();
  183. layout.addComponent(widgetContainer);
  184. widgetContainer.addComponent(new Label(
  185. "Click the button to add components to the WidgetContainer."));
  186. Button button = new Button("Add more components", new ClickListener() {
  187. @Override
  188. public void buttonClick(ClickEvent event) {
  189. Random randomGenerator = new Random();
  190. int random = randomGenerator.nextInt(3);
  191. Component component;
  192. if (random % 3 == 0) {
  193. component = new Label("A new label");
  194. } else if (random % 3 == 1) {
  195. component = new Button("A button!");
  196. } else {
  197. component = new CheckBox("A textfield");
  198. }
  199. widgetContainer.addComponent(component);
  200. }
  201. });
  202. layout.addComponent(button);
  203. }
  204. }
  205. ....