I have a simple wrapper around a WYSIWYG editor (TinyMCE). I'm using JSNI to call a Java instance method (onClick) from Javascript. However the Java onClick method always gets called on the same Java instance (the last one created), no matter the editor that originated it.
private SimplePanel panel;
private TextArea ta;
private String id;
public TinyMCE(AbstractTinyMCEConfiguration config) {
id = HTMLPanel.createUniqueId();
ta = new TextArea();
ta.getElement().setId(id);
panel = new SimplePanel();
panel.add(ta);
initWidget(panel);
init(config);
}
protected native void init(AbstractTinyMCEConfiguration conf) /*-{
var ins = this;
$wnd.tinyMCE.init({
// General options
mode : conf.#com.chip.tinymce.client.AbstractTinyMCEConfiguration::getMode()(),
setup : function(ed) {
ed.onClick.add(function(ed,e) {
alert(ed.id);
ins.#com.chip.tinymce.client.TinyMCE::onClick(Lcom/google/gwt/dom/client/NativeEvent;)(e);
});
}
});
}-*/;
private void onClick(NativeEvent e) {
GWT.log("onClick " + id);
ClickEvent.fireNativeEvent(e, this);
}
I'm not sure if I can call a Java method from a Javascript funcion that is inside another funcion. Maybe that explains my problem... or maybe I'm missing something. Thanks for your help.
I think TinyMCE has one shared configuration for all editors, and that is the problem here.
It probably does not make much sense to hand the configuration to the constructor if it is shared...
Why not add a static map that maps the id back to the Java instance, something like
// ....
private static Map<String, TinyMCE> idMap = new HashMap<String, TinyMCE>();
public TinyMCE() {
// ...
idMap.put(id, this);
}
// call this from Javascript with (ed.id, e)
private static void onClick(String id, NativeEvent e) {
idMap.get(id).onClick(e);
}
Related
public class SettingMailPage {
#UiField
ButtonElement save;
#UiField
AnchorElement input;
SettingMailPage()
{
bindActions(save.cast(),input.cast());
}
private native void bindActions(JavaScriptObject save, JavaScriptObject input)
/*-{
$wnd.$(save).click(function () {
$wnd.alert($wnd.$(input).size());//always 0, why?
});
}-*/;
}
I'd like to know why the bind action works while in callback it fail to select that element, and any workaround. Thanks
EDIT:
private native void bindActions(JavaScriptObject save, JavaScriptObject input)
/*-{
var thatInput=input;
$wnd.$(save).click(function () {
$wnd.alert($wnd.$(thatInput).size());
});
}-*/;
will work, but not know the reason, could someone explain?
As bindActions is evaluated before the click callback, the callback has no chance accessing the input in the state it was when passed to the method.
The only way to do so is locking the state by holding a reference (like you smartly did), or wrapping the entire binding in a closure (terribly cumbersome and redundant), which will look like this:
private native void bindActions(JavaScriptObject save, JavaScriptObject input) /*-{
(function(in) {
$wnd.$(save).click(function () {
$wnd.alert($wnd.$(in).size());
});
})(input);
}-*/;
It's all about context; remember that inside the callback, you're referring to the function owner (which is save button). See the ridiculously famous article on quirksmode on the subject.
I am trying to invoke a Java Method from my Javascript code. This is for a Windows Phone 7 app using Phonegap.
I have the following in my javascript code.
document.addEventListener("backbutton", onBackKeyDown, false);
function onBackKeyDown(){
}
And in my Java code I have the following.
public static native void exportStaticMethod() /*-{
$wnd.onBackKeyDown =
$entry(this.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets());
}-*/;
Then in the on onModuleLoad() I am calling it like so:
MyApp.exportStaticMethod();
It does not work I have an alert in the hideSettingsWidgets() but it never gets shown.
*EDIT*
Here is some more code. The EventListener is not added in the Javascript. It is specifically added withing the java code. I could not get the listeners to register originally so here is what I added.
public static native void removeBackListener() /*-{
$wnd.removeTheListener();
}-*/;
And in my JavaScript
function removeTheListener(){
document.removeEventListener("backbutton", onBackKeyDown, false);
}
Here is my call to hideSettingsWidgets()
public void hideSettingsWidgets(){
for(int i=0;i<settingsScreenWidgets.length;i++){
settingsScreenWidgets[i].setVisible(false);
}
alertString("Working");
removeBackListener();
}
And I am calling the method you gave me inside showSettingsWidgets()
p
rivate void showSettingsWidgets(){
for(int i=0;i<settingsScreenWidgets.length;i++){
settingsScreenWidgets[i].setVisible(true);
}
setCurrentImage();
setOnOffImage();
setupJavaHandler();
}
It does seem to be adding the EventListener that is inside your
public native void setupJavaHandler() /*-{
var app = this;
var onBackKeyDown = $entry(function() {
app.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets();
});
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
So I am not sure where I am going wrong. I did not add the ArrayList<> you mentioned because was not sure to and the Event Listener was not running when page was Loaded.
Seems like showSettingsWidgets() never gets run
The addEventListener code is probably running when the page loads, right? This will map your empty function onBackKeyDown to the backbutton event. Then, when your module loads, you attempt to redefine the onBackKeyDown function to be a new one - but the old one was already attached to the event you are trying to listen to.
This is roughly the equivalent of this (with strings instead of listener functions):
// first, make the thing to hold the 'listener', and define the first one
List<String> strings = new ArrayList<String>();
String onBackKeyDown = "abcd";
strings.add(onBackKeyDown);
// then, redefine the string, but don't change the list!
onBackKeyDown = "zyxw";
assert strings.contains(onBackKeyDown) : "Whoops, reassigned, but not added!";
To fix this, you need a cross between what you are doing in your other question, Adding Eventlisteners to document with GWT JSNI, and what you are doing here. Wrapping the Java function in an $entry call, and passing that to $doc.addEventListener makes the most logical sense (though I don't know a lot about WP7).
public static native void setupJavaHandler() /*-{
var onBackKeyDown = $entry(this.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets());
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
One more thing - remembering that we are writing JavaScript in that native code, what is going to be this when that hideSettingsWidgets() method is called? JavaScript doesn't know that all Java instance methods need a this to run on (and JavaScript has no problem running methods for object A on B - A.method.call(B) is totally legal, and often helpful). We need to be sure that this means what we think it does:
public static native void setupJavaHandler() /*-{
var app = this;
var onBackKeyDown = $entry(function() {
app.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets();
});
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
Edit: Oops, turns out your method was static anyway, so this doesn't actually mean anything! Either change exportStaticMethod/setupJavaHandler to be non static and call it directly (probably in your onModuleLoad as you have it now), or pass in an instance to call hideSettingsWidgets() on, like we are doing with app in the previous sample.
public native void setupJavaHandler() /*-{
var app = this;
var onBackKeyDown = $entry(function() {
app.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets();
});
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
// in onModuleLoad:
setupJavaHandler();
or
public static native void setupJavaHandler(MpApp app) /*-{
//var app = this;
var onBackKeyDown = $entry(function() {
app.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets();
});
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
// in onModuleLoad:
MyApp.setupJavaHandler(this);
I am trying to invoke a Java Method from my Javascript code. This is for a Windows Phone 7 app using Phonegap.
I have the following in my javascript code.
document.addEventListener("backbutton", onBackKeyDown, false);
function onBackKeyDown(){
}
And in my Java code I have the following.
public static native void exportStaticMethod() /*-{
$wnd.onBackKeyDown =
$entry(this.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets());
}-*/;
Then in the on onModuleLoad() I am calling it like so:
MyApp.exportStaticMethod();
It does not work I have an alert in the hideSettingsWidgets() but it never gets shown.
*EDIT*
Here is some more code. The EventListener is not added in the Javascript. It is specifically added withing the java code. I could not get the listeners to register originally so here is what I added.
public static native void removeBackListener() /*-{
$wnd.removeTheListener();
}-*/;
And in my JavaScript
function removeTheListener(){
document.removeEventListener("backbutton", onBackKeyDown, false);
}
Here is my call to hideSettingsWidgets()
public void hideSettingsWidgets(){
for(int i=0;i<settingsScreenWidgets.length;i++){
settingsScreenWidgets[i].setVisible(false);
}
alertString("Working");
removeBackListener();
}
And I am calling the method you gave me inside showSettingsWidgets()
p
rivate void showSettingsWidgets(){
for(int i=0;i<settingsScreenWidgets.length;i++){
settingsScreenWidgets[i].setVisible(true);
}
setCurrentImage();
setOnOffImage();
setupJavaHandler();
}
It does seem to be adding the EventListener that is inside your
public native void setupJavaHandler() /*-{
var app = this;
var onBackKeyDown = $entry(function() {
app.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets();
});
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
So I am not sure where I am going wrong. I did not add the ArrayList<> you mentioned because was not sure to and the Event Listener was not running when page was Loaded.
Seems like showSettingsWidgets() never gets run
The addEventListener code is probably running when the page loads, right? This will map your empty function onBackKeyDown to the backbutton event. Then, when your module loads, you attempt to redefine the onBackKeyDown function to be a new one - but the old one was already attached to the event you are trying to listen to.
This is roughly the equivalent of this (with strings instead of listener functions):
// first, make the thing to hold the 'listener', and define the first one
List<String> strings = new ArrayList<String>();
String onBackKeyDown = "abcd";
strings.add(onBackKeyDown);
// then, redefine the string, but don't change the list!
onBackKeyDown = "zyxw";
assert strings.contains(onBackKeyDown) : "Whoops, reassigned, but not added!";
To fix this, you need a cross between what you are doing in your other question, Adding Eventlisteners to document with GWT JSNI, and what you are doing here. Wrapping the Java function in an $entry call, and passing that to $doc.addEventListener makes the most logical sense (though I don't know a lot about WP7).
public static native void setupJavaHandler() /*-{
var onBackKeyDown = $entry(this.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets());
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
One more thing - remembering that we are writing JavaScript in that native code, what is going to be this when that hideSettingsWidgets() method is called? JavaScript doesn't know that all Java instance methods need a this to run on (and JavaScript has no problem running methods for object A on B - A.method.call(B) is totally legal, and often helpful). We need to be sure that this means what we think it does:
public static native void setupJavaHandler() /*-{
var app = this;
var onBackKeyDown = $entry(function() {
app.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets();
});
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
Edit: Oops, turns out your method was static anyway, so this doesn't actually mean anything! Either change exportStaticMethod/setupJavaHandler to be non static and call it directly (probably in your onModuleLoad as you have it now), or pass in an instance to call hideSettingsWidgets() on, like we are doing with app in the previous sample.
public native void setupJavaHandler() /*-{
var app = this;
var onBackKeyDown = $entry(function() {
app.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets();
});
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
// in onModuleLoad:
setupJavaHandler();
or
public static native void setupJavaHandler(MpApp app) /*-{
//var app = this;
var onBackKeyDown = $entry(function() {
app.#com.mycompany.myapp.client.MyApp::hideSettingsWidgets();
});
$doc.addEventListener("backbutton", onBackKeyDown, false);
}-*/;
// in onModuleLoad:
MyApp.setupJavaHandler(this);
I would like to define the GWT JSO property name as a constant in the JSO, in order to avoid typos and benefit from Eclipse code completion, like so:
public final class MyJSO extends JavaScriptObject
{
/** here is the constant */
private static final String MY_CONST = "myPropName";
protected MyJSO() {
super();
}
public native void setMyProp(final boolean pFlag)
/*-{
this.#fully.qualified.MyJSO::MY_CONST = pFlag;
}-*/;
public native boolean isMyProp()
/*-{
if (this.hasOwnProperty(#fully.qualified.MyJSO::MY_CONST)) {
return this.#fully.qualified.MyJSO::MY_CONST;
} else {
return false;
}
}-*/;
}
The GWT compiler should be able to replace the String from the constant at compile time, so there is no problem with the object living as Javascript later on.
But this is so totally not working, I'm thinking I may be wrong. :-) Can anyone explain why? Do you have better ideas how to achieve this?
Thanks!
The correct syntax to refer to a static variable is:
#fully.qualified.MyJSO::MY_CONST
No qualifier (this., in your example) is needed since the variable is static.
If you want to set/get a property on the JavaScript object with the constant name do so as follows:
public native void setMyProp(final boolean pFlag) /*-{
this[#fully.qualified.MyJSO::MY_CONST] = pFlag;
}-*/;
public native boolean isMyProp() /*-{
if (this[#fully.qualified.MyJSO::MY_CONST] != null) {
return this[#fully.qualified.MyJSO::MY_CONST];
} else {
return false;
}
}-*/;
I am wanting to use the phonegap audio api in GWT using JSNI.I cannot figure out how to code the methods in JSNI.
Wondering if anyone know of any tutorials.They javascript methods are really pretty simple.
http://docs.phonegap.com/phonegap_media_media.md.html
Basically it sounds like it would be something like this:
public final class Media extends JavaScriptObject {
protected Media() {}
public static native final Media newInstance(String src, Command command) /*-{
var callback = function() { command.execute(); };
return new Media(src, callback);
}-*/;
public native final void getCurrentPosition(AsyncCallback<String> command) /*-{
var callback = function(position) { command.onSuccess('' + position); };
this.getCurrentPosition(callback);
}-*/;
public native final void play() /*-{
this.play();
}-*/;
//... more methods here
}
Usage:
Media m = Media.newInstance("http://www.example.com/src.mp3", new Command() {
#Override
public void execute() {
// Code executed after Media is created.
}
});
m.getCurrentPosition(new AsyncCallback<String>() {
#Override
public void onSuccess(String position) {
Window.alert(position);
}
});
m.play();
That's a rough sketch, if you know more about what the type being passed to the callback is you can do nicer things like have it be an int or another JS Overlay Type.
The API is kind of weird because everything is apparently asynchronous, but that's life.
Once you've gotten the hang of writing GWT JSNI bindings it's pretty straightforward.
If you end up getting further down this road, it would be awesome if you open-sourced your GWT wrapper library so other GWT developers could write some iPhone/Android apps.
I just need the play method really.I am not quite as knowledgeable to do this correctly I guess.That code looks really foreign to me :-)
Still cannot accept your answer.The site does not recognize me it is strange.
I get the following error when trying to use the media in my onModuleLoad
The constructor TESTPHONEGAP.Media(String, new Command(){}) is undefined
Media m = new Media("test.mp3", new Command() {
#Override
public void execute() {
}
});
m.play()
Using your class as an "inner class" in same file as my main onModuleLoad