From 7e21ed054f40a18f43bc715915e9231c9a450cdd Mon Sep 17 00:00:00 2001
From: Erik Lumme <erik@vaadin.com>
Date: Wed, 13 Sep 2017 12:39:49 +0300
Subject: Migrate CreatingASimpleComponentContainer

---
 .../CreatingASimpleComponentContainer.asciidoc     | 241 +++++++++++++++++++++
 1 file changed, 241 insertions(+)
 create mode 100644 documentation/articles/CreatingASimpleComponentContainer.asciidoc

(limited to 'documentation/articles/CreatingASimpleComponentContainer.asciidoc')

diff --git a/documentation/articles/CreatingASimpleComponentContainer.asciidoc b/documentation/articles/CreatingASimpleComponentContainer.asciidoc
new file mode 100644
index 0000000000..1d3cb9c372
--- /dev/null
+++ b/documentation/articles/CreatingASimpleComponentContainer.asciidoc
@@ -0,0 +1,241 @@
+[[creating-a-simple-component-container]]
+Creating a simple component container
+-------------------------------------
+
+Components in Vaadin can be roughly split into two groups, `Component`{empty}s
+and `ComponentContainer`{empty}s. ComponentContainers are Components in
+themselves which can also contain other components. If you are about to
+implement a component that contains other components, then you'll get a
+headstart by extending Vaadin's `ComponentContainer`. The biggest feature
+is in tranferring the list of server side components from your component
+to the client. Here's how you do it.
+
+[[server-side]]
+Server Side
+^^^^^^^^^^^
+
+To start of we implement our server side component. For this we extend
+the ready made abstract implementation `AbstractComponentContainer`. This
+requires us to implement `addComponent(Component)`,
+`removeComponent(Component)`, `replaceComponent(Component, Component)`,
+`getComponentCount` and `getComponentIterator()`.
+
+[source,java]
+....
+package com.example.widgetcontainer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.vaadin.ui.AbstractComponentContainer;
+import com.vaadin.ui.Component;
+
+public class WidgetContainer extends AbstractComponentContainer {
+
+  List<Component> children = new ArrayList<Component>();
+
+  @Override
+  public void addComponent(Component c) {
+    children.add(c);
+    super.addComponent(c);
+    markAsDirty();
+  }
+
+  @Override
+  public void removeComponent(Component c) {
+    children.remove(c);
+    super.removeComponent(c);
+    markAsDirty();
+  }
+
+  public void replaceComponent(Component oldComponent, Component newComponent) {
+    int index = children.indexOf(oldComponent);
+    if (index != -1) {
+      children.remove(index);
+      children.add(index, newComponent);
+      fireComponentDetachEvent(oldComponent);
+      fireComponentAttachEvent(newComponent);
+      markAsDirty();
+    }
+  }
+
+  public int getComponentCount() {
+    return children.size();
+  }
+
+  public Iterator<Component> iterator() {
+    return children.iterator();
+  }
+}
+....
+
+Add, remove and replace are quite straight forward. In the class we
+upkeep a list of children internally, and these three methods modify
+them. Add and remove have ready made methods in the super class for
+notifying all event handlers that the children have changed and because
+of that we should make calls to the super methods after we have updated
+the list. In `replaceComponent` we have to call
+`fireComponentDetachEvent(Component)` and
+`fireComponentAttachEvent(Component)` to manually trigger these events. In
+all three methods we should also call `markAsDirty` as a last step to
+notify the client side that the children have changed.
+
+The methods `getComponentCount()` and `iterator()` takes care of providing
+the required information that we need to the client side. Here they are
+simple delegate methods to the List's `size()` and `iterator()`.
+
+[[client-side]]
+Client Side
+^^^^^^^^^^^
+
+Next up, we want to set up a standard GWT widget which will be our
+component container's client side widget. GWT in itself has a bunch of
+component containers in it. In GWT, these are called Panels. For this
+case I will start with a `VerticalPanel`. It is roughly the same as
+`VerticalLayout` in Vaadin. Down the road you want to edit this file to
+add features or even extend Widget to create a complete custom widget.
+For now extending `VerticalPanel` is enough and we'll use that as-is.
+
+[source,java]
+....
+package com.example.widgetcontainer.client.ui;
+
+import com.google.gwt.user.client.ui.VerticalPanel;
+
+public class VWidgetContainer extends VerticalPanel {
+  public static final String CLASSNAME = "v-widgetcontainer";
+
+  public VWidgetContainer() {
+    setStyleName(CLASSNAME);
+  }
+}
+....
+
+[[connector]]
+Connector
+^^^^^^^^^
+
+Your widget's Connector will transfer the components from the server
+side as child widgets to our widget. The connector will feed the
+children to the panel trough it's standard API, namely `add(Widget)`,
+`remove(Widget)` and `clear();`
+
+Instead of going the standard route of extending
+`AbstractComponentConnector` as your connector, here we can take use of
+Vaadin's internal features and extend
+`AbstractComponentContainerConnector`. Additionally to implementing the
+`getWidget()` -method from `AbstractComponentConnector`, we also have to
+supply the class with an implementation to a method called
+`updateCaption(ComponentConnector)`. This method is there if we want the
+container to take care of the captions for all the components. We don't
+need to take care of these captions in this example so we can leave the
+implementation empty.
+
+The real benefit of extending `AbstractComponentContainerConnector` is
+that we can now extend a method called
+`onConnectorHierarchyChange(ConnectorHierarchyChangeEvent)`. This method
+will be called every time that the server side calls `markAsDirty()` if
+the component hierarchy has been changed. From within it, we can call on
+`getChildComponents` to get a list of all the child components, and
+populate our widget with those.
+
+[source,java]
+....
+package com.example.widgetcontainer.client.ui;
+
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.Widget;
+import com.example.widgetcontainer.WidgetContainer;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ConnectorHierarchyChangeEvent;
+import com.vaadin.client.ui.AbstractComponentContainerConnector;
+import com.vaadin.client.ui.Connect;
+
+@Connect(WidgetContainer.class)
+public class WidgetContainerConnector extends
+        AbstractComponentContainerConnector {
+
+  @Override
+  public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
+    List<ComponentConnector> children = getChildComponents();
+    VWidgetContainer widget = getWidget();
+    widget.clear();
+    for (ComponentConnector connector : children) {
+      widget.add(connector.getWidget());
+    }
+  }
+
+  @Override
+  public VWidgetContainer getWidget() {
+    return (VWidgetContainer) super.getWidget();
+  }
+
+  public void updateCaption(ComponentConnector connector) {
+  }
+}
+....
+
+This implementation removes all the component's in the widget and adds
+all that are returned from `getChildComponents`. An obvious optimization
+to these is to compare what is already in the widget and only
+add/remove/move those widgets that have changed.
+
+[[example-usage]]
+Example Usage
+^^^^^^^^^^^^^
+
+Nothing left but to use the component! Compile the widgetset and check
+that the widgetset is in use in your web.xml. Here is a little
+stand-alone application that uses this component:
+
+[source,java]
+....
+package com.example.widgetcontainer;
+
+import java.util.Random;
+
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.UI;
+
+public class WidgetcontainerUI extends UI {
+  @Override
+  public void init(VaadinRequest request) {
+    VerticalLayout layout = new VerticalLayout();
+    layout.setMargin(true);
+    setContent(layout);
+
+    Label label = new Label("Hello Vaadin user");
+    layout.addComponent(label);
+    final WidgetContainer widgetContainer = new WidgetContainer();
+    layout.addComponent(widgetContainer);
+    widgetContainer.addComponent(new Label(
+        "Click the button to add components to the WidgetContainer."));
+    Button button = new Button("Add more components", new ClickListener() {
+      @Override
+      public void buttonClick(ClickEvent event) {
+        Random randomGenerator = new Random();
+        int random = randomGenerator.nextInt(3);
+        Component component;
+        if (random % 3 == 0) {
+          component = new Label("A new label");
+        } else if (random % 3 == 1) {
+          component = new Button("A button!");
+        } else {
+          component = new CheckBox("A textfield");
+        }
+        widgetContainer.addComponent(component);
+      }
+    });
+    layout.addComponent(button);
+  }
+}
+....
-- 
cgit v1.2.3

