Android Question Wait a Javascript value

max123

Well-Known Member
Licensed User
Longtime User
Hi all,

I 've ported this @stevel05 B4J CodeMirror wrapper to B4A
https://www.b4x.com/android/forum/threads/codemirror-wrapper-and-example.125775/#post-785682

Now I need to get values back from Javascript but I've some problems.

Steve in his B4J code implemented itself a Javascript interface on a JavaFX WebView, ExecuteJavaScript command can execute JS and return back an object so it can back with just one call.

B4X:
'Executes a script in the context of the current page.
Public Sub ExecuteScript(Script As String) As Object
    Return TJO.RunMethod("executeScript",Array As Object(Script))
End Sub

B4J CodeMirrorWrapper to get editor code do this:
B4X:
'Get the current code editor text code
Public Sub GetCode() As String
    Return ExecuteScript("editor.getValue();")  ' A single call to inject JS and get back return value to B4J calling module that request it (Main or class module)
End Sub

In my code I use WebViewExtras and ExecuteJavascript method do not return values, so I just created a JS function to get a value and then JS call a B4A sub to pass back it.

The problem here is that this require some time and I do not know how to wait a value.
It always return non last value assigned to a global variable (from previous call).

I've even tried to reset it and wait with while loop (not good option, just for test) while the value change, but as expected it enter in an infinite loop.

Even I do not like that require a global variable.

Here my attemp:
B4X:
'Get the current text on this CodeEditor.
Public Sub GetCode As String
    '    Code = ""  ' Code is a String global variable
    wve.executeJavascript(mWebView, "getValue();")

'    Return wve.executeJavascript(mWebView, "editor.getValue();"  ' This cannot be used because ExecuteJavascript
                                                                ' method return nothing
    Return Code
End Sub

Public Sub codemirrortext(Value As String)  ' Called by JS to return back the value
    Code = Value
    Log(">>> CodeMirror text from JS: " & CRLF & Code)
End Sub

JS Code:
function GetValue() {
  var text = editor.getValue();
  B4A.CallSub('codemirrortext', true, text);
}

I need to do it on a lots of methods (in the wrapper class).

Wait For I think cannot help here because the sub is called by JS and not B4A, right ?

Please can someone help me to know how to do it in an elegant way ?

Thanks
 
Last edited:

max123

Well-Known Member
Licensed User
Longtime User
Mmmmm... don't like this, encode then decode.

In past I've used a comma separated string between JS and B4X and worked well, tried up 10 arguments, but teorically there's no limit, just browser memory I think.

On B4A side just used regex.split to split the array. I've posted it on the forum, I need to search it.

So you intend on B4A side a single callback and use first argument as ID. Not bad idea.

Many thanks for your advices.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
In past I've used a comma separated string between JS and B4X
I think there may be some edge cases that could fail depending on the content of the strings, if they are all plain ascii text it shouldn't be an issue.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Not always are strings, can be Boolean, Int and more, no custom types, maybe null.

This can help, but the main problem remain, that is to get a value without necessary use one global variable for any value passed back. And find a way to get it in the main without necessary use WaitFor.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Not always are strings, can be Boolean, Int and more, no custom types, maybe null
You'll find out what you can and can't pass when you start testing it, and may have to convert some types for them to work.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Probably a stupid question.... but B4A Object and JS object are compatible ? I think no but not sure, they probably completely different object structure
 
Last edited:
Upvote 0

stevel05

Expert
Licensed User
Longtime User
No not compatible, if I remember correctly you will get at best, a string representation, So a boolean value may be passed as "true" or "false".
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
B4X:
Sub JSCallback(ID As String, Arg1 As String)
  Select ID
    Case "getMyBoolean"
      If Arg1 = "true" Then
        globalBoolean = True
      Else
        globalBoolean = False
      End If
End Sub
Not always is a problem, with threejs I pass B4X color as Int or Colors.MyColor or Color.RGB(r, g, b) and JS works like a charm., so eg I can color a 3D mesh red by passing Colors.Red or Colors.RGB(255, 0, 0). Even booleans worked.
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
How to avoid use global variables ?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Why do you need global variables?
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
to assign a returnig value and return back to the main that request it:
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
If you need to act on the values returned, can't you just call the appropriate sub directly?
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I do not know what you mean.
In the main I just need:
B4X:
Dim Code As String = CodeEditor1.GetCode
without Wait For, as your old work on codemirror, just like I can get a label.Text or EditText.Text I want to get CodeEditor.Text, nothing other.... I've improved your wrapper, now I added more setters and getters, add code folding and syntax error check for some languages, these added new getters and setters not strictly necessary but useful.

So you think I can avoid to use global variables to store returning values in the callback to next pass to the main in the called public sub ? If yes it's good, but how ?
 
Last edited:
Upvote 0

stevel05

Expert
Licensed User
Longtime User
So you'd do something like:

B4X:
CodeEditor1.GetCode
Wait For Code_Received(Data As String)
Log(Data)

And the callback sub would be something like:

B4X:
Sub JSCallback(ID As String, Arg1 As String)
  Select ID
    Case "CodeReceived"
      CallSubDelayed2(Main,"Code_Received",Arg1)
End Sub
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Yes, but require to use Wait For in the main:
B4X:
Wait For CodeReceived(Data As String)
At this point it require to get everything in codemirror, Theme, Language, cursor position, text selection and more....
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Yes, it has to wait for the reply.
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
And it will keep the program flow. How would you know where to pick up the flow if you're assigning the values to a global variable?
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
adding a jsRunning global bool variable as @teddybear suggested on post #13 ?
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
That still uses a wait for and is more complicated. You can use whichever you prefer, I'd prefer the simple version.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Yes, thanks, I will try it , your solution do not require a boolean flag šŸ‘

So no way to remove Wait For in the main, it need because return a ResumableSub
 
Last edited:
Upvote 0

stevel05

Expert
Licensed User
Longtime User
In my version it is not a resumable sub per se as there is no return value, the wait is for a call from a different sub.
 
Last edited:
Upvote 0
Top