In GWT how can we share objects between javascript and java? - gwt

I have a pojo in my class containing some methods to manipulate Maps and Arrays in java. This object is used in RPC calls to carry my configurations. I have a mechanism in which before making any RPC call I execute a javascript function. Now what I really want is to pass my configuration object to this javascript function and this javascript function can manipulate this configuration object and finally this manipulated object will be passed in my RPC call.
So how can I pass my java object to javascript and allow manipulating it?

First, you cannot manipulate Java objects from javascript directly. But what you can do, is to export a set of static methods to javascript and use them to manipulate your objects. This is done in this way:
public void onModuleLoad() {
exportHelloMethod(this);
}
public String exportedMethod(String name) {
// Manipulate your java classes here
// return something to JS
}
// Create a reference in the browser to the static java method
private native void exportHelloMethod(HelloClass instance) /*-{
$wnd.hello = instance#[...]HelloClass::exportedMethod(Ljava/lang/String;);
}-*/;
Fortunately there is a library which allows exporting java methods and classes in a simpler way. It is gwt-exporter, and you have just to implement Exportable in your class and use a set of annotations so as the exporter generator does all the work.
#ExportPackage("jsc")
#Export
public class MyClass implements Exportable {
public void show(String s){
}
}
public void onModuleLoad() {
ExporterUtil.exportAll();
}
Then in javascript you can instanciate and manipulate the class:
var myclass = new jsc.MyClass();
myclass.show('whatever');

Related

Can I define a reusable subroutine/function/method within a Cake script?

I'm trying out Cake (C# Make). So far all the examples and documentation have the script file declaring all of its code inside delegates, like this:
Task("Clean")
.Does(() =>
{
// Delete a file.
DeleteFile("./file.txt");
// Clean a directory.
CleanDirectory("./temp");
});
However, one of the reasons I'm interested in using Cake is the possibility of writing my build scripts in a similar way to how I write code, as the scripts use a C#-based DSL. Included in this possibility is the ability to separate code that I use into methods (or functions / subroutines, whatever terminology is appropriate) so I can separate concerns and reuse code. For example, I may want to run the same set of steps for a multiple SKUs.
While I realize that I could create my own separate DLL with Script Aliases, I would like to avoid having to recompile a separate project every time I want to change these bits of shared code when working on the build script. Is there a way to define, inline with the normal build.cake file, methods that can still run the Cake aliases (e.g., DeleteFile) and can themselves be called from my Cake tasks?
Cake is C#, so you can create classes, methods, just like in regular C#
I.e. declare a class in a cake file
public class MyClass
{
public void MyMethod()
{
}
public static void MyStaticMethod()
{
}
}
and then use it a script like
var myClass = new MyClass();
// Call instance method
myClass.MyMethod();
//Call static method
MyClass.MyStaticMethod();
The Cake DSL is based on Roslyn scripting so there are some differences, code is essentially already in a type so you can declare a method without a class for reuse
public void MyMethod()
{
}
and then it can be called like a global methods
MyMethod();
A few gotchas, doing class will change scoping so you won't have access to aliases / context and global methods. You can get around this by i.e. passing ICakeContext as a parameter to class
public class MyClass
{
ICakeContext Context { get; }
public MyClass(ICakeContext context)
{
Context = context;
}
public void MyMethod()
{
Context.Information("Hello");
}
}
then used like this
// pass reference to Cake context
var myClass = new MyClass(Context);
// Call instance method which uses an Cake alias.
myClass.MyMethod();
You can have extension methods, but these can't be in a class, example:
public static void MyMethod(this ICakeContext context, string message)
{
context.Information(message);
}
Context.MyMethod("Hello");

Usage of JsniBundle: calling methods on initialized js library

When I initialize d3.js and dc.js using JsniBundle there is no global variable "dc" or "d3" that is created. But I initialze crossfilter in the same way and there is window.crossfilter present.
My question is: what is the best way to call methods from the dc library using JsniBundle? Is using JsUtils.prop(window, "dc") the correct way to get a reference to the library object?
In the method drawBarChartWidget() below, the variable "dc" is null.
public interface D3Bundle extends JsniBundle {
#LibrarySource("d3.js")
public void initD3();
}
public interface CrossfilterBundle extends JsniBundle {
#LibrarySource("crossfilter.js")
public abstract void initCrossfilter();
}
public abstract static class DCBundle implements JsniBundle {
#LibrarySource("dc.js")
public abstract void initDC();
public void drawBarChart(Widget container, JSONValue data, Properties chartConfig) {
JavaScriptObject dc = JsUtils.prop(window, "dc");
}
}
LayoutPanel layoutPanel = new LayoutPanel();
SimplePanel chartPanel = new SimplePanel();
public ChartDemo() {
D3Bundle d3 = GWT.create(D3Bundle.class);
CrossfilterBundle crossfilter = GWT.create(CrossfilterBundle.class);
final DCBundle dc = GWT.create(DCBundle.class);
d3.initD3();
crossfilter.initCrossfilter();
dc.initDC();
Maybe not a direct answer to your question, but if you want to use d3.js with GWT, there is a wrapper that cover most of the main APIs from d3.js :
https://github.com/gwtd3/gwt-d3
Here's what made it work:
change final assignment statement in d3.js library from
this.d3 = d3;
to
window.d3 = d3;
and change final assignment statement in dc.js library from
this.dc = _dc(d3);
to
window.dc = _dc(window.d3);
I assume this is because of some weirdness around the iframe context that GWT code is executed in, but I'm not totally clear on why it works. I haven't done it yet but I believe that instead of editing the original library you can use the "replace" attribute of the LibrarySource annotation to automate that substitution.

Load a ListBox content dynamically on page load

I'm currently working on a simple GWT project. One of the things I'd like to do is that when the page loads I can dynamically populate the contents of a ListBox based on certain criteria. I actually don't see any handlers for a ListBox to handle the initial render event but I see change handlers.
How does one populate a ListBox contents with data from the server side on pageload with GWT?
Right now I have a class that implements EntryPoint that has a
final ListBox fooList = new ListBox();
I also have a set of beans but I also have a class implementing RemoteService. Since I can't seem to get direct calls to my user defined packages directly in the EntryPoint (which makes sense) how do I populate that ListBox with server side content on initial page load? Right now I'm using a List but I figure if I cant get that to work I can get a DB call to work...
I've tried things in the EntryPoint like:
for (String name : FOOS) {
fooList.addItem(name, name);
}
However FOOS would derive from a server side data and the EntryPoint is supposed to be largerly limited to what can compile to JS! I can't get user defined classes to be recognized on that side as that string is the result of a set of user defined classes.
I also tried creating a method in the class implementing RemoteService that returns a ListBox. This also didn't compile when I tried to call this method. Perhaps I don't fully understand how to call methods in a RemoteService service implementing class.
I've searched a lot and I can't find anything that clearly explains the fundamentals on this. My background is much more ASP.NET and JSPs so perhaps I'm missing something.
I'm using GWT 2.6 is that is relevant.
The usual procedure is the following:
Create a bean class for the data you want to transmit between client and server. Let's call it MyBean.
Place MyBean in the shared package of your project.
This class has to implement either Serializable or IsSerializable, otherwise GWT will complain that it doesn't know how to transmit it.
Create your RemoteService that contains the method you want to use to transmit MyBean from/to the server.
Once you get your data on the client using an AsyncCallback and your RemoteService, fill the ListBox using your beans, e.g. by calling MyBean#getName() or MyBean#toString().
Success!
I based my example on the GWT sample project ( I named it example), just replace the classes and it should work :
public class Example implements EntryPoint {
/**
* Create a remote service proxy to talk to the server-side Greeting
* service.
*/
private final GreetingServiceAsync greetingService = GWT
.create(GreetingService.class);
/**
* This is the entry point method.
*/
public void onModuleLoad() {
final ListBox listBox = new ListBox();
RootPanel.get("sendButtonContainer").add(listBox);
greetingService.getSomeEntries(new AsyncCallback<String[]>() {
#Override
public void onSuccess(String[] result) {
for (int i = 0; i < result.length; i++) {
listBox.addItem(result[i]);
}
}
#Override
public void onFailure(Throwable caught) {
}
});
}
}
This is our EntryPoint, it creates a listbox and calls the server with a AsyncCallback to get some dynamic data. If the call is successfull (onSuccess), the data is written into the listbox.
The GreetingService interface define the synchronous methods, it is implemented in the GreetingServiceImpl class :
#RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
String[] getSomeEntries() ;
}
The asynchronous counterpart is the GreetingServiceAsync interface, we used it before to call the server :
public interface GreetingServiceAsync {
void getSomeEntries(AsyncCallback<String[]> callback) ;
}
The GreetingServiceImpl class is located on the server. Here you could call for example a database:
#SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
GreetingService {
#Override
public String[] getSomeEntries() {
String[] entries = { "Entry 1","Entry 2","Entry 3" };
return entries;
}
}
Now if you want to use some Bean/Pojo between the server and client, replace the String[] in each class/interface with the object name, put the class in the shared package and consider that it implements Serializable/IsSerializable.

How to implement a javascript API using JSNI?

I am trying to implement an API (SCORM API) using GWT.
The client code expects an API object with methods like Initialize(), getLastError() and so on...
I tried to implement this api as an Java Object, but i see that the compiled names are changed and cannot be used directly by client code.
I see that gwt-exporter can do the trick (http://code.google.com/p/gwt-exporter/) but i would like to know how to do it using pure gwt and jsni.
As the API is expected as a object, named API_1484_11 attached to the window object, not an function, , i don't see how to use the $entry() idiom.
Here is my current, failing, code:
public final class SCORMApi {
protected SCORMApi() {}
public void Initialize(){
GWT.log("** INITIALIZE CALLED **");
}
public static void create(){
bind(new SCORMApi());
}
public static native void bind(SCORMApi api) /*-{
$wnd.API_1484_11 = api;
}-*/;
}
So, in this context, my question is:
How can i get javascript calls (e.g. window.API_1484_11.Initialize() ) to reach my java gwt code?
You're on the right lines with your bind method. But you haven't understood how to call Java methods from within JSNI. This is how you do it in the case of your Initialize method:
public static native void bind(SCORMApi api) /*-{
$wnd.API_1484_11 = {
initialize: function() {
$entry( api.#com.yourpackage.name.SCORMApi::Initialize()() );
}
};
}-*/;
The blogs Getting To Really Know GWT parts 1 and 2 are required reading on this subject.

Linking to GWT instance method from JSNI does not automatically bind "this"

I am going to file this as a bug report, but I wanted to check if someone here can see something wrong with what I am doing.
When you expose an instance method from a GWT class through JSNI, this works as expected in JavaScript. Since we are cross compiling Java, I would instead expect this to be bound to the instance automatically. For example:
package com.test;
class Foo {
public void instanceFunction() {
this.otherFunction() // will cause an error when called from JSNI!
}
public void otherFunction() {
// does some stuff
}
public native JavaScriptObject getInstanceFunction() /*-{
return this.#com.test.Foo::instanceFunction();
}-*/;
}
Currently the workaround is to bind the function yourself (not very portable):
public native JavaScriptObject getInstanceFunction() /*-{
return this.#com.test.Foo::instanceFunction().bind(this);
}-*/;
This can also be seen as preference, some may prefer that the functions remain unbound. I would say the current functionality is unintuitave and unnecessary. I cannot imagine a use case for having an unbound this directly in Java code. Also, some browsers do not implement bind(1), so my workaround is not robust.
If you want a portable bind, it's as easy as:
var that = this;
return $entry(function() {
return that.#com.test.Foo::instanceFunction()();
});