Calling javascript function with arguments using JSNI - javascript

I'm trying to integrate Mixpanel with GWT, but I have problems calling an event with a property and one value.
My function to track an simple event (without values):
public native void trackEvent(String eventName)/*-{
$wnd.mixpanel.track(eventName);
}-*/;
It works.
But when I want to add some properties and values, it doesn't work properly:
public native void trackComplexEvent(String eventName, String property,
String value)/*-{
$wnd.mixpanel.track(eventName, {property:value});
}-*/;
I have 2 problems with this:
1) Mixpanel says the property name is: "property"(yes, the name of the variable that I'm passing, not the value).
2) Mixpanel says the value is:undefined
An example from mixpanel web is:
mixpanel.track("Video Play", {"age": 13, "gender": "male"});
So, I guess the problem is I'm doing a wrong call or with wrong type of arguments.

your problem is, that when you pass the you create the property object, you don't create a parameter, taken from your property name, but rather a property named property. If you debug your code, you can check, that a parameter property is passed to your mixpanel.track function.
To do what you want, you have to use an other syntax.
public native void trackComplexEvent(String eventName, String property,
String value)/*-{
//create the property object you want to pass
var propertyPassed = {}
// set the value you want to pass on the propertyPassed object
propertyPassed[property] = value;
//call your function with the argumetn you want to pass
$wnd.mixpanel.track(eventName, propertyPassed);
}-*/;
I tested your code, by creating a JavaScript funciton on my site:
window.mixpanel = {
track : function(eventName, props) {
alert(props.age);
}
}
and calling it with
trackComplexEvent("hallo", "age", "13");
The alert was '13'
BR,
Stefan

Related

Extending Java Classes and Overriding Methods with Overloaded Parameters in ES4X/Graal

I'm trying to extend a Java class in a JavaScript project using ES4X/Graal. The class that I want to extend, has methods with overloeaded parameters that I need to override. I know that you can call a specific Java method by using the square brackets notation and specifying the type (example below), but, apparently, there's no way to specify the parameter types when overriding, per the Graal/Oracle/Nashorn documentation. So if you have the following:
package com.mycomp;
class A {
public void method(String parameter) {...}
public void method(int parameter) {...}
}
you can call either method in JavaScript like so:
var a = Java.type("com.mycomp.A");
a["method(String)"]("Some string parameter")
a["method(int)"](42)
a.method(stringOrIntParam)
However, when extending, you can only do the following:
var ClassAFromJava = Java.type("com.mycom.A");
var MyJavaScriptClassA = Java.extend(ClassAFromJava, {
method: function(stringOrIntParam) {...}
}
I want to be able to extend only one of the method(...), um, methods. And what about overloaded methods with different return types?
Thanks!
The sj4js allows you to do all that.
This example is slightly modified from the documentation...
public class TestObject extends JsProxyObject {
// the constructor with the property
public TestObject (String name) {
super ();
this.name = name;
// we hvae to initialize the proxy object
// with all properties of this object
init(this);
}
// this is a mandatory override,
// the proxy object needs this method
// to generate new objects if necessary
#Override
protected Object newInstance() {
return new TestClass(this.name);
}
// a method with a String parameter
public String method (String s) {
return "String";
}
// a method with a int parameter
public String method (int i) {
return "42";
}
}
And you can access this object as you would access a JS object and
the library takes care of selecting the appropriate method.
try (JScriptEngine engine = new JScriptEngine()) {
engine.addObject("test", new TestClass("123"));
// calling the method with the String parameter would
// result in calling the appropriate java method
engine.exec("test.method('123')");
// returns "String"
// calling the method with the int parameter would
// result in calling the appropriate java method
engine.exec("test.method(123)");
// returns "42"
}

Casting to a java object in Mozilla Rhino

I'm wondering is there a way of casting a Java object in Rhino? I would usually do this in Java by doing the following:
List<Object[]> mylist = myServiceClass.getList(id);
for (Object[] object : myList) {
Apple a = (Apple) object[0];
// do something
}
But in Rhino, I'm not sure. Is there a way to do this?
I've tried:
apple = myList.get(i)
And:
apple = (Packages.com.package.fruits.Apple) myList.get(i);
I keep getting the error
Java class "[Ljava.lang.Object;" has no public instance field or method named "getColour" when trying to access the apple object.
You don't need to cast in javascript. But you need to call the method on the correct object. The error message states that you call the method on an Object[] (The [ before L gives that away).
To get the first object from Object[] before you call getColour() on it
myList.get(i)[0].getColour()

Nashorn assign a Java function to a map

I have this engine which runs user-input (trusted) javascript function to filter some data for them using Nashorn. Don't want to go into the details of the requirements but let's say it's some sort of plugin system.
This javascript gets a Java map(which acts like a JS object) as a parameter to get some contextual data.
So I want to add some convenience extension methods to this JS object for the users. So I did the following:
Map<String, String> inputMap = ....
inputMap.put("containsMagic", (Function<String, Boolean>) param -> some complex magic check and return boolean);
Works perfectly!
if(inputMap.containsMagic('Im Por Ylem'))
do stuff;
However, I want it to accept no parameter as well, because null is a valid value and containsMagic() looks better than containsMagic(null).
if(inputMap.containsMagic())
do stuff;
But I get this:
TypeError: Can not invoke method ......$$Lambda$15/1932831450#30c15d8b with the passed arguments; they do not match any of its method signatures.
I guess that's kind of normal given how Java works. If I pass null, it works of course but that's not very intuitive.
I can't add another function with 0 parameters and the same name because Java maps are single keyed. The complex magic check needs to be in java, so I can't assign a javascript function (JSObject?) either. Any ideas how I can accomplish this?
You can implement containsMagic as explicit method with a varargs parameter:
public static class InputMap extends HashMap<String, Object> {
public Boolean containsMagic(String... args) {
String arg = args.length == 1 ? args[0] : null;
// do magic
}
}
...
Map<String, Object> inputMap = new InputMap();
java.util.function unfortunately lacks a standard interface for variadic functions, but you could add your own:
#FunctionalInterface
public interface VariadicFunction<T,R> {
public R apply(T... args);
}
and then
inputMap.put("containsMagic", (VariadicFunction<String, Boolean>) param -> some complex magic check and return boolean);

Cast a javascript object into a model of the controller

I created an object in my js file:
function Shape(name, color, count) {
this.Name = name;
this.Color = color;
this.Count = count;
}
var apples = new Shape('apple', 'green', 5);
I want to pass this "class" into the controller via load'function, so I wrote:
$('#myDiv').load(url, { 'Shape': apples });
The url is the url of a function in the controller:
function GetShape(shape as ShapeModel) as actionResult
End Function
While ShapeModel is:
Class ShapeModel
Public Property Name As String
Public Property Color As String
Public Property Count As Integer
Public Property IsHot As Boolean
End Class
The function is called by the load function but the parameters of the class are equal to nothing.
I want to convert the class that came in the js file (Shape) into the class in the controller(ShapeModel) In the function above (GetShape).
These classes have the same properties, except one property (IsHot).
How can I do it?
I tried to send the class of the js as JSON.stringify and then make a deserialization in the controller from the given string into the class of ShapeModel, but the deserializtion failed..
The optimal solution is to send it as a class and not stringify that..
Any help appreciated!
I believe that your issue is in how you are passing the parameters in the jQuery load call. You are wrapping your Shape object in another object with a Shape property. Instead, just pass the Shape object directly as the data.
$('#myDiv').load(url, apples);
I think that MVC only supports binding a single parameter from the body, so you don't need to have something that specifies the parameter name.

Backbone set method, pass a variable as the key?

Im trying to pass an argument which would dynamically set the key in the backbone set method.
I pass it as a string into the constructor of my function like this
this.keyStat('points', 1)
That passes points, but when I set the model it creates the attribute stat not recognizing it as a variable.
keyStat: function(stat, number) {
var addStat = parseInt(this.model.get(stat)) + number;
console.log(this.model.set({stat: addStat}));
}
I am trying to build this function so that I dont have to repeat a lot of code and bloat my project, but I am not sure how I can pass that into the set method so that it recognizes my argument.
My question is how do I pass an argument/variable to the backbone set methods key?
set method can take a key as a first argument and value as a second (unless first argument is an object)
keyStat: function(stat, number) {
var addStat = parseInt(this.model.get(stat)) + number;
console.log(this.model.set(stat, addStat));
}

Categories