B4J Tutorial [BANano] Calling JavaScript methods with parameters explained

Suppose we use the plotly library (https://plotly.com) as an example to add a chart.

When we look at their example on how to use it:
B4X:
Plotly.newPlot( "chart", [{
    x: [1, 2, 3, 4, 5],
    y: [1, 2, 4, 8, 16] }], {
    margin: { t: 0 } }, {showSendToCloud:true} );

How would that translate to B4J and BANano?

The newPlot function can have a variable number of parameters. But because b4J can not do that, we have to pass an Array() if we have a variable number of parameters.
Literally typing Array() here is important in the runMethod call, see (*) !

Looking at their code we need 4 parameters in the newPlot() call:
1. "chart"
2. [{x: [1, 2, 3, 4, 5], y: [1, 2, 4, 8, 16] }]
3. {margin: { t: 0 } }
4. {showSendToCloud:true}

So in B4J this would translate as:
B4X:
Dim element As String = "chart" ' <--- this is de ID of a div tag where we want the chart, see further
Dim coords As List = Array(CreateMap("x": xData, "y": yData))
Dim margin As Map = CreateMap("margin": CreateMap("t": 0))
Dim settings As Map = CreateMap("showSendToCloud": True)

Now we can call:
B4X:
plot.RunMethod("newPlot", Array(element, coords, margin, settings ))

(*) You may be inclined to do the following but it transpiles to something different:
B4X:
Dim params As List
params.Initialize
params.Add("chart")
params.Add(Array(CreateMap("x": xData, "y": yData)))
params.Add(CreateMap("margin": CreateMap("t": 0)))
params.Add(CreateMap("showSendToCloud": True))

plot.RunMethod("newPlot", params)

This will not work! Why? Just because we need a workaround for the variable number of parameters limitation. The array() in the parameters is important so the BANano transpiler knows it has to 'spread' all elements in the params variable as seperate parameters of the function.

So this call:
B4X:
plot.RunMethod("newPlot", Array(element, coords, margin, settings ))

will be transpiled to:
B4X:
plot.newPlot(element, coords, margin, settings)  ' correct

While this call:
B4X:
plot.RunMethod("newPlot", params)

will be transpiled to:
B4X:
plot.newPlot([element,coords,margin,settings]) ' note the [], not correct

What would've worked is forcing a 'spread' of the parameters using BANano.Spread():
B4X:
plot.RunMethod("newPlot", BANano.Spread(params))

This would also have been transpiled to:
B4X:
plot.newPlot(element, coords, margin, settings)  ' correct

BANano.Spread() forces the transpiler to 'spread' all the variables in Params. But in my view, this is a bit 'JavaScripty' so I prefer the more B4J way of using just seperate variables.

My full code would be:
B4X:
Sub Process_Globals
    Dim plot as BANanoObject
End Sub

Sub AppStart(Form1 as Form, Args() as String)
    ...
    BANano.Header.AddJavascriptFile("https://cdn.plot.ly/plotly-latest.min.js")
End sub

Sub BANano_Ready()
    ...
    InitializePlotly(Array As Double(1, 2, 3, 4, 5),Array As Double(1, 2, 4, 8, 16))
End Sub

Public Sub InitializePlotly(xData() As Double, yData() As Double)
    plot.Initialize("Plotly")

    Dim body As BANanoElement
    body.Initialize("#body")
    body.Append($"<div id="chart" style="width:600px;height:250px;"></div>"$)

    Dim element As String = "chart"
    Dim coords As List = Array(CreateMap("x": xData, "y": yData))
    Dim margin As Map = CreateMap("margin": CreateMap("t": 0))
    Dim settings As Map = CreateMap("showSendToCloud": True)

    plot.RunMethod("newPlot", Array(element, coords, margin, settings ))
End Sub

Result:
1615886671919.png


I hope this makes it a bit clearer how we can circumvent the variable parameters limitation and how to call such a function in B4J.

Alwaysbusy
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
But in my view, this is a bit 'JavaScripty'

Thanks a lot for that. I've seen a lot of these "spread" calls in what I'm doing with VueJS and was wondering...

I like that at times you have to go outside of the box so that we can also realize that what we are trying to achieve is possible with B4J and BANano, just like the array functions you added, which are very crucial shortcuts in JavaScript. Perhaps one day when anyone wants to start to build anything JavaScript, they will first think BANano.

Hi 5!!! šŸ™
 

alwaysbusy

Expert
Licensed User
Longtime User
As an alternative if you do not feel comfortable to fill the variables with Array and CreateMap because e.g. the objects are very complex, you can also use #If JavaScriptSmart

It is something I wrote back in the days but never use myself as I like doing things the B4J way, but it is perfectly usable here:

B4X:
Public Sub InitializePlotly(xData() As Double, yData() As Double, Title As String)
    plot.Initialize("Plotly")
    
    Dim body As BANanoElement
    body.Initialize("#body")
    body.Append($"<div id="chart" style="width:600px;height:250px;"></div>"$)
    
    Dim element As String = "chart"
    Dim coords As List
    Dim margin As Map
    Dim settings As Map
    
    #If JavaScriptSmart
        ${coords} = [{x: [1, 2, 3, 4, 5], y: [1, 2, 4, 8, 16] }]
        ${margin} = {margin: { t: 0 } }
        ${settings} = {showSendToCloud:true}
    #End If
    
    plot.RunMethod("newPlot", Array(element, coords, margin, settings ))
End Sub

It acts like B4J Smart Strings, so you can use use the real B4J variable names in the ${} (not with _ and lowercased like you have to do with the normal #If JavaScript).

NOTE: The same can be done with #If PHPSmart and #If CSSSmart

Alwaysbusy
 

NGUYEN TUAN ANH

Active Member
Licensed User
I have an JavaScript: leaflet.rotatedMarker.js
leaflet.rotatedMarker.js:
(function() {
    // save these original methods before they are overwritten
    var proto_initIcon = L.Marker.prototype._initIcon;
    var proto_setPos = L.Marker.prototype._setPos;

    var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');

    L.Marker.addInitHook(function () {
        var iconOptions = this.options.icon && this.options.icon.options;
        var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
        if (iconAnchor) {
            iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
        }
        this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom' ;
        this.options.rotationAngle = this.options.rotationAngle || 0;

        // Ensure marker keeps rotated during dragging
        this.on('drag', function(e) { e.target._applyRotation(); });
    });

    L.Marker.include({
        _initIcon: function() {
            proto_initIcon.call(this);
        },

        _setPos: function (pos) {
            proto_setPos.call(this, pos);
            this._applyRotation();
        },

        _applyRotation: function () {
            if(this.options.rotationAngle) {
                this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;

                if(oldIE) {
                    // for IE 9, use the 2D rotation
                    this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
                } else {
                    // for modern browsers, prefer the 3D accelerated version
                    this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';
                }
            }
        },

        setRotationAngle: function(angle) {
            this.options.rotationAngle = angle;
            this.update();
            return this;
        },

        setRotationOrigin: function(origin) {
            this.options.rotationOrigin = origin;
            this.update();
            return this;
        }
    });
})();
Could you please help me call it in B4J to rotate leaflet marker icon
 
Top