Integrating JavaScript Components and Extensions

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.

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 MyComponent component, defined in mylibrary.js.

  1. // Define the namespace
  2. var mylibrary = mylibrary || {};
  3. mylibrary.MyComponent = function (element) {
  4. element.innerHTML =
  5. "<div class='caption'>Hello, world!</div>" +
  6. "<div class='textinput'>Enter a value: " +
  7. "<input type='text' name='value'/>" +
  8. "<input type='button' value='Click'/>" +
  9. "</div>";
  10. // Style it
  11. element.style.border = "thin solid red";
  12. element.style.display = "inline-block";
  13. // Getter and setter for the value property
  14. this.getValue = function () {
  15. return element.
  16. getElementsByTagName("input")[0].value;
  17. };
  18. this.setValue = function (value) {
  19. element.getElementsByTagName("input")[0].value =
  20. value;
  21. };
  22. // Default implementation of the click handler
  23. this.click = function () {
  24. alert("Error: Must implement click() method");
  25. };
  26. // Set up button click
  27. var button = element.getElementsByTagName("input")[1];
  28. var self = this; // Can't use this inside the function
  29. button.onclick = function () {
  30. self.click();
  31. };
  32. };

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

  1. <script type="text/javascript"
  2. src="mylibrary.js"></script>

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

  1. <!-- Placeholder for the component -->
  2. <div id="foo"></div>
  3. <!-- Create the component and bind it to the placeholder -->
  4. <script type="text/javascript">
  5. window.foo = new mylibrary.MyComponent(
  6. document.getElementById("foo"));
  7. window.foo.click = function () {
  8. alert("Value is " + this.getValue());
  9. }
  10. </script>

javascript component

A JavaScript Component Example

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

  1. <a href="javascript:foo.setValue('New value')">Click here</a>

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.

  1. final MyComponent mycomponent = new MyComponent();
  2. // Set the value from server-side
  3. mycomponent.setValue("Server-side value");
  4. // Process a value input by the user from the client-side
  5. mycomponent.addValueChangeListener(
  6. new MyComponent.ValueChangeListener() {
  7. @Override
  8. public void valueChange() {
  9. Notification.show("Value: " + mycomponent.getValue());
  10. }
  11. });
  12. layout.addComponent(mycomponent);

Basic Server-Side Component

A JavaScript component extends the AbstractJavaScriptComponent, which handles the shared state and RPC for the component.

  1. package com.vaadin.book.examples.client.js;
  2. @JavaScript({"mylibrary.js", "mycomponent-connector.js"})
  3. public class MyComponent extends AbstractJavaScriptComponent {
  4. public interface ValueChangeListener extends Serializable {
  5. void valueChange();
  6. }
  7. ArrayList<ValueChangeListener> listeners =
  8. new ArrayList<ValueChangeListener>();
  9. public void addValueChangeListener(
  10. ValueChangeListener listener) {
  11. listeners.add(listener);
  12. }
  13. public void setValue(String value) {
  14. getState().value = value;
  15. }
  16. public String getValue() {
  17. return getState().value;
  18. }
  19. @Override
  20. protected MyComponentState getState() {
  21. return (MyComponentState) super.getState();
  22. }
  23. }

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:

  1. public class MyComponentState extends JavaScriptComponentState {
  2. public String value;
  3. }

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

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.//TOD Clarify - code?

A connector is defined as a connector initializer function that is added to the 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 this.getElement() method returns the HTML DOM element of the component. The this.getState() returns a shared state object with the current state as synchronized from the server-side.

  1. window.com_vaadin_book_examples_client_js_MyComponent =
  2. function() {
  3. // Create the component
  4. var mycomponent =
  5. new mylibrary.MyComponent(this.getElement());
  6. // Handle changes from the server-side
  7. this.onStateChange = function() {
  8. mycomponent.setValue(this.getState().value);
  9. };
  10. // Pass user interaction to the server-side
  11. var self = this;
  12. mycomponent.click = function() {
  13. self.onClick(mycomponent.getValue());
  14. };
  15. };

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

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 “RPC Calls Between Client- and Server-Side”.

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 JavaScriptFunction interface. A server-side function handler is registered with the addFunction() method in AbstractJavaScriptComponent. The server-side registration actually defines a JavaScript method that is available in the client-side connector object.

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

  1. public MyComponent() {
  2. addFunction("onClick", new JavaScriptFunction() {
  3. @Override
  4. public void call(JsonArray arguments) {
  5. getState().setValue(arguments.getString(0));
  6. for (ValueChangeListener listener: listeners)
  7. listener.valueChange();
  8. }
  9. });
  10. }

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):

  1. window.com_vaadin_book_examples_gwt_js_MyComponent =
  2. function() {
  3. ...
  4. var connector = this;
  5. mycomponent.click = function() {
  6. connector.onClick(mycomponent.getValue());
  7. };
  8. };

Here, the mycomponent.click is a function in the example JavaScript library, as described in Example JavaScript Library. The 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.