summaryrefslogtreecommitdiffstats
path: root/documentation/articles/IntegratingAnExistingGWTWidget.asciidoc
blob: 81889f98340ab8b150d78034604253e63bc00c6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
---
title: Integrating An Existing GWT Widget
order: 33
layout: page
---

[[integrating-an-existing-gwt-widget]]
= Integrating an existing GWT widget

Integrating an existing, third party GWT widget usually just involves
creating a regular Vaadin component with a client-side connector that
uses the third-party widget, probably using a shared state, and possibly
also with RPC - just as described in separate articles on these topics.
Usually, the only addition is the need to modify your widgetset
declaration to inherit the third-party widget's GWT module.

In the following, we'll integrate the SimplePlot widget from the
http://code.google.com/p/gflot/[GFlot library] (which in turn is a GWT
adaptation of the pure JavaScript plotting library Flot) to create a
simple line plot component. We'll start with modifying our widgetset's
`gwt.xml` to inherit the GFlot GWT module, so if you're familiar with the
rest of the process, you can basically stop once that is done.

But first a note on package structure: this particular example uses the
`com.example` package domain, and is set up to be a add-on project, with
the actual component in the `addon` package, and the demo that uses the
component in the `vaadingflot` package. The `addon` package contains the
`widgetset gwt.xml` definition, the server-side component (LinePlot), as
well as all the code related to the client-side in the `client.ui`
subpackage.

Once you have a working project, go ahead and
http://code.google.com/p/gflot/downloads/list[download the GFlot jar],
and add it to `WEB-INF/lib`, then update the widgetset `gwt.xml` as follows:

[source,xml]
....
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.0//EN"
         "http://google-web-toolkit.googlecode.com/svn/tags/1.7.0/distro-source/core/src/gwt-module.dtd">
<module>
  <inherits name="com.vaadin.DefaultWidgetSet" />
  <inherits name="ca.nanometrics.gflot.GFlot" />
</module>
....

It inherits the default Vaadin widgetset as well as the GFlot GWT
module.

Now we're ready to integrate the SimplePlot widget from the GFlot
package. Since we're familiar with the GFlot API, we know we want to add
'series' to the plot - we'll create a shared state for this purpose, and
a `DataSeries` class to represent the actual series within the state:

[source,java]
....
package com.example.addon.client.ui;

import java.util.ArrayList;
import java.util.List;

import com.vaadin.client.ComponentState;

public class LinePlotState extends AbstractComponentState {

  public List<DataSeries> series = new ArrayList<DataSeries>();

  public static class DataSeries {
    public String label;
    public String color;
    public List<Float> data;
  }
}
....

Lets make the server-side component next:

[source,java]
....
package com.example.addon;

import java.util.Arrays;

import com.example.addon.client.ui.LinePlotState;
import com.example.addon.client.ui.LinePlotState.DataSeries;
import com.vaadin.ui.AbstractComponent;

public class LinePlot extends AbstractComponent {

  public LinePlot() {
  }

  @Override
  public LinePlotState getState() {
    return (LinePlotState) super.getState();
  }

  public void addSeries(String label, String color, Float[] fs) {
    DataSeries ds = new DataSeries();
    ds.label = label;
    ds.color = color;
    ds.data = Arrays.asList(fs);
    getState().series.add(ds);
  }
}
....

We override `getState()` in order to narrow the return type to our own
`LinePlotState`, and then implement a simple `addSeries()` that creates a
`DataSeries` instance and adds it to the state. The state will be
automatically transmitted to the client when needed, so the plots will
remain intact over browser reloads for instance.The API for our
component could obviously be expanded, but lets leave it like this for
this example.

Since the GWT widget we're going to use is already made for us (in the
GFlot library), the only thing left for us to do is implement the
client-side connector:

[source,java]
....
package com.example.addon.client.ui;

import ca.nanometrics.gflot.client.DataPoint;
import ca.nanometrics.gflot.client.SeriesHandler;
import ca.nanometrics.gflot.client.SimplePlot;

import com.example.addon.LinePlot;
import com.example.addon.client.ui.LinePlotState.DataSeries;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.Connect;

@Connect(LinePlot.class)
public class LinePlotConnector extends AbstractComponentConnector {

  @Override
  public LinePlotState getState() {
    return (LinePlotState) super.getState();
  }

  @Override
  public SimplePlot getWidget() {
    return (SimplePlot) super.getWidget();
  }

  @Override
  protected Widget createWidget() {
    return GWT.create(SimplePlot.class);
  }

  @Override
  public void onStateChanged(StateChangeEvent stateChangeEvent) {
    super.onStateChanged(stateChangeEvent);

    getWidget().getModel().clear();
    for (DataSeries ds : getState().series) {

      SeriesHandler s = getWidget().getModel().addSeries(ds.label,
              ds.color);
      for (int i = 0; i < ds.data.size(); i++) {
        s.add(new DataPoint(i, ds.data.get(i)));
      }
    }
    getWidget().redraw();
  }
}
....

We override both `getState()` and `getWidget()` to narrow the return type to
our liking, then make `createWidget()` return an instance of the GFlot
widget we're going to use, `SimplePlot`.

Last, we override `onStateChange()` which is called whenever the shared
state has been changed. Here we make use of the `SimplePlot` API to add
the series contained in the shared state (for simplicity, we clear the
`SimplePlot` first, then add all the series in our state).

That's it! The full source is available as an attachment to this
article.

link:img/vaadingflot.zip[Attachment vaadingflot.zip]