summaryrefslogtreecommitdiffstats
path: root/documentation/articles/ExposingServerSideAPIToJavaScript.asciidoc
blob: 7d996f357fbaab823041ff34615a13b0c230d8de (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
---
title: Exposing Server Side API To JavaScript
order: 41
layout: page
---

[[exposing-server-side-api-to-javascript]]
= Exposing server-side API to JavaScript

The new JavaScript integration functionality will allow you to easily
publish methods that can be called with JavaScript on the client side.
In effect, you can publish a JavaScript API for your application.
Although you will probably not find yourself using this very often, it
can be useful when integrating with JavaScript frameworks or embedding
within legacy sites.

Exposing a `notify()` method that takes a message and displays that as a
notification can be done in one simple block in e.g `UI.init()`:

[source,java]
....
JavaScript.getCurrent().addFunction("notify", new JavaScriptFunction() {
  public void call(JSONArray arguments) throws JSONException {
    Notification.show(arguments.getString(0));
  }
});
....

This will expose the `notify()`{empty}-method globally in the window object.
Technically it's thus `window.notify()`, but you can call it by simply
by `notify()`. Try entering `notify("Hey!")` into the Firebug or
Developler Tools console, or `javascript:notify("Hey!")` into the
address bar.

You'll notice that this assumes there is a String in the first position
of the array. Also, this will clutter the global namespace, which is
generally not a good idea, unless you really have a specific need for
that.

Let's make a complete example with two arguments, some simple error
handling, and namespacing:

[source,java]
....
JavaScript.getCurrent().addFunction("com.example.api.notify",
  new JavaScriptFunction() {
    public void call(JSONArray arguments) throws JSONException {
      try {
        String caption = arguments.getString(0);
        if (arguments.length() == 1) {
            // only caption
            Notification.show(caption);
        } else {
            // type should be in [1]
            Notification.show(caption,
                Type.values()[arguments.getInt(1)]);
        }
      } catch (JSONException e) {
        // We'll log in the console, you might not want to
        JavaScript.getCurrent().execute(
            "console.error('" + e.getMessage() + "')");
      }
    }
  });
}
....

Using the dotted notation for the method will automatically create those
objects in the browser; you'll call this method like so:
`com.example.api.notify("Hey!")`. You do not have to use a long name
like this, though - it's up to you and your use-case.

The second thing to notice is that we now wrapped the code in a
try-catch, so that the wrong number or wrong types of arguments does not
cause an ugly stacktrace in our server logs. Again, how you should react
to erroneous use of your exposed API depends on your use-case. We'll log
an error message to the browser console as an example.

We're now accepting a second (integer) argument, and using that as
_type_ for the `Notification`.

Finally, we'll add a link that will call the function, and work as a
_Bookmarklet_. You can drag the link to your bookmarks bar, and when you
invoke it when viewing the application with our exposed `notify()`{empty}-method, you will be prompted for a message that will then be sent to
the method. Here is the plain HTML code for creating such a link:

[source,html]
....
<a href="javascript:(function(){com.example.api.notify(prompt('Message'),2);})();">Send message</a>
....

Here is the full source for our application:

[source,java]
....
import org.json.JSONArray;
import org.json.JSONException;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.JavaScript;
import com.vaadin.ui.JavaScriptFunction;
import com.vaadin.ui.Link;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Notification.Type;
import com.vaadin.ui.UI;

public class JSAPIUI extends UI {
  @Override
  public void init(VaadinRequest request) {

    JavaScript.getCurrent().addFunction("com.example.api.notify",
        new JavaScriptFunction() {
          public void call(JSONArray arguments) throws JSONException {
            try {
              String caption = arguments.getString(0);
              if (arguments.length() == 1) {
                // only caption
                Notification.show(caption);
              } else {
                // type should be in [1]
                Notification.show(caption,
                    Type.values()[arguments.getInt(1)]);
              }
            } catch (JSONException e) {
              // We'll log in the console, you might not want to
              JavaScript.getCurrent().execute(
                  "console.error('" + e.getMessage() + "')");
            }
          }
        });


    setContent(new Link(
        "Send message",
        new ExternalResource(
            "javascript:(function(){com.example.api.notify(prompt('Message'),2);})();")));
  }
}
....