aboutsummaryrefslogtreecommitdiffstats
path: root/documentation/gwt/gwt-javascript.asciidoc
blob: 2dead86f072e1d166749cc35f53611d31713cfbe (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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
---
title: Integrating JavaScript Components and Extensions
order: 13
layout: page
---

[[gwt.javascript]]
= Integrating JavaScript Components and Extensions

((("JavaScript integration", id="term.gwt.javascript", range="startofrange")))


Vaadin allows simplified integration of pure JavaScript components, as well as
component and UI extensions. The JavaScript connector code is published from the
server-side. As the JavaScript integration does not involve GWT programming, no
widget set compilation is needed.

[[gwt.javascript.example]]
== Example JavaScript Library

There are many kinds of component libraries for JavaScript. In the following, we
present a simple library that provides one object-oriented JavaScript component.
We use this example later to show how to integrate it with a server-side Vaadin
component.

The example library includes a single [classname]#MyComponent# component,
defined in [filename]#mylibrary.js#.

[source,javascript]
----
// Define the namespace
var mylibrary = mylibrary || {};

mylibrary.MyComponent = function (element) {
	element.innerHTML =
		"<div class='caption'>Hello, world!</div>" +
		"<div class='textinput'>Enter a value: " +
		"<input type='text' name='value'/>" +
		"<input type='button' value='Click'/>" +
		"</div>";

	// Style it
	element.style.border = "thin solid red";
	element.style.display = "inline-block";

	// Getter and setter for the value property
	this.getValue = function () {
		return element.
		    getElementsByTagName("input")[0].value;
	};
	this.setValue = function (value) {
		element.getElementsByTagName("input")[0].value =
		    value;
	};

	// Default implementation of the click handler
	this.click = function () {
		alert("Error: Must implement click() method");
	};

	// Set up button click
	var button = element.getElementsByTagName("input")[1];
	var self = this; // Can't use this inside the function
	button.onclick = function () {
		self.click();
	};
};
----

When used in an HTML page, the library would be included with the following
definition:

[source,html]
----
<script type="text/javascript"
        src="mylibrary.js"></script>
----

You could then use it anywhere in the HTML document as follows:

[source,html]
----
<!-- Placeholder for the component -->    
<div id="foo"></div>
    
<!-- Create the component and bind it to the placeholder -->
<script type="text/javascript">
    window.foo = new mylibrary.MyComponent(
            document.getElementById("foo"));
    window.foo.click = function () {
        alert("Value is " + this.getValue());
    }
</script>
----

[[figure.gwt.javascript.example]]
.A JavaScript Component Example
image::img/javascript-component.png[]

You could interact with the component with JavaScript for example as follows:

[source,html]
----
<a href="javascript:foo.setValue('New value')">Click here</a>
----


[[gwt.javascript.server-side]]
== A Server-Side API for a JavaScript Component

To begin integrating such a JavaScript component, you would need to sketch a bit
how it would be used from a server-side Vaadin application. The component should
support writing the value as well as listening for changes to it.

[source,java]
----
final MyComponent mycomponent = new MyComponent();

// Set the value from server-side
mycomponent.setValue("Server-side value");

// Process a value input by the user from the client-side
mycomponent.addValueChangeListener(
        new MyComponent.ValueChangeListener() {
    @Override
    public void valueChange() {
        Notification.show("Value: " + mycomponent.getValue());
    }
});

layout.addComponent(mycomponent);
----

[[gwt.javascript.server-side.component]]
=== Basic Server-Side Component

A JavaScript component extends the [classname]#AbstractJavaScriptComponent#,
which handles the shared state and RPC for the component.

[source,java]
----
package com.vaadin.book.examples.client.js;

@JavaScript({"mylibrary.js", "mycomponent-connector.js"})
public class MyComponent extends AbstractJavaScriptComponent {
    public interface ValueChangeListener extends Serializable {
        void valueChange();
    }
    ArrayList<ValueChangeListener> listeners =
            new ArrayList<ValueChangeListener>();
    public void addValueChangeListener(
                   ValueChangeListener listener) {
        listeners.add(listener);
    }
    
    public void setValue(String value) {
        getState().value = value;
    }
    
    public String getValue() {
        return getState().value;
    }

    @Override
    protected MyComponentState getState() {
        return (MyComponentState) super.getState();
    }
}
----

Notice later when creating the JavaScript connector that its name must match the
package name of this server-side class.

The shared state of the component is as follows:

[source,java]
----
public class MyComponentState extends JavaScriptComponentState {
    public String value;
}
----

If the member variables are private, you need to have public setters and getters
for them, which you can use in the component.



[[gwt.javascript.connector]]
== Defining a JavaScript Connector

A JavaScript connector is a function that initializes the JavaScript component
and handles communication between the server-side and the JavaScript code.
//TODO Clarify - code?

A connector is defined as a connector initializer function that is added to the
[literal]#++window++# object. The name of the function must match the
server-side class name, with the full package path. Instead of the Java dot
notation for the package name, underscores need to be used as separators.

The Vaadin client-side framework adds a number of methods to the connector
function. The [methodname]#this.getElement()# method returns the HTML DOM
element of the component. The [methodname]#this.getState()# returns a shared
state object with the current state as synchronized from the server-side.

[source,javascript]
----
window.com_vaadin_book_examples_client_js_MyComponent =
function() {
    // Create the component
    var mycomponent =
        new mylibrary.MyComponent(this.getElement());
    
    // Handle changes from the server-side
    this.onStateChange = function() {
        mycomponent.setValue(this.getState().value);
    };

    // Pass user interaction to the server-side
    var self = this;
    mycomponent.click = function() {
        self.onClick(mycomponent.getValue());
    };
};
----

In the above example, we pass user interaction using the JavaScript RPC
mechanism, as described in the next section.


[[gwt.javascript.rpc]]
== RPC from JavaScript to Server-Side

User interaction with the JavaScript component has to be passed to the
server-side using an RPC (Remote Procedure Call) mechanism. The JavaScript RPC
mechanism is almost equal to regular client-side widgets, as described in
<<gwt-rpc#gwt.rpc,"RPC Calls Between Client- and
Server-Side">>.

[[gwt.javascript.rpc.handling]]
=== Handling RPC Calls on the Server-Side

Let us begin with the RPC function registration on the server-side. RPC calls
are handled on the server-side in function handlers that implement the
[interfacename]#JavaScriptFunction# interface. A server-side function handler is
registered with the [methodname]#addFunction()# method in
[classname]#AbstractJavaScriptComponent#. The server-side registration actually
defines a JavaScript method that is available in the client-side connector
object.

Continuing from the server-side [classname]#MyComponent# example we defined
earlier, we add a constructor to it that registers the function.

[source,java]
----
public MyComponent() {
    addFunction("onClick", new JavaScriptFunction() {
        @Override
        public void call(JsonArray arguments) {
            getState().setValue(arguments.getString(0));
            for (ValueChangeListener listener: listeners)
                listener.valueChange();
        }
    });
}
----


[[gwt.javascript.rpc.calling]]
=== Making an RPC Call from JavaScript

An RPC call is made simply by calling the RPC method in the connector. In the
constructor function of the JavaScript connector, you could write as follows
(the complete connector code was given earlier):

[source,javascript]
----
window.com_vaadin_book_examples_gwt_js_MyComponent =
    function() {
        ...
        var connector = this;
        mycomponent.click = function() {
            connector.onClick(mycomponent.getValue());
        };
    };
----

Here, the [literal]#++mycomponent.click++# is a function in the example
JavaScript library, as described in <<gwt.javascript.example>>. The
[methodname]#onClick()# is the method we defined on the server-side. We pass a
simple string parameter in the call.

You can pass anything that is valid in JSON notation in the parameters.



(((range="endofrange", startref="term.gwt.javascript")))