Returning Data to Xojo Web App

Thanks @Anthony_G_Cyphers for this. Testing it out I used this exact code (you will see that I am responding to the Web 1.0 version of the code) and implementing a msgbox in place of the break in the currentValue setter, I notice that the msgbox displays in time with a Xojo framework serverEvent - in other words once the Xojo serverEvent has gone to the server (unsure if I can use the ‘completed’ word here as I don’t know but would assume so) then the msgbox appears.

Is this a case of the variable (in this example) gets set pretty much instantly but for framework reasons, the msgbox instruction needs to ‘wait’ for a Xojo serverEvent ?

Thanks,
Steve

I’m honestly not sure, but it would make sense to me that setting in this way wouldn’t immediately send the data for the WebMessageDialog and would require some other action on that Session. @Greg_O_Lone could answer this in more detail, but, like I said, it makes sense to me.

That’s exactly it. In web 1, things that got assigned on a non web thread were queued until the next event or request from the client came in. Due to the fact that it’s only an http/1.0 server, that will be between 5 and 15 seconds from when the value was set.

Thanks @Greg_O_Lone. Is this actually due to http/1.0 or a design choice though out of interest ?

(Not an expert on the standards :grinning:)

A more generic solution using the event JavaScriptError in your session Instance:

For example:

Sub JavaScriptError(ErrorName as String, ErrorMessage as String, ErrorStack as String) Handles JavaScriptError
  
  
  if ErrorName = "json-data" and ErrorStack.Middle(0,1) = "{" then
    dim data as new jsonItem(ErrorStack)
    
    
    //do something with data
    System.DebugLog "name=" + data.value("name")
  end if
End Sub

In javascript you can use this helper function:

function sendDataToXojo(data){
	o = new XojoWeb.JSONItem(); o.set("message", "json-data"); o.set("name", "json-data"); o.set("stack", JSON.stringify(data));
	console.log(o);
	XojoWeb.session.comm.triggerServerEvent("Event","JavaScriptError", o);
}

You can use you App (web application) html header property to inject something like this:

<script>
var _loadCallbacks = [];
var a = setInterval(function(){
	if(!window.XojoWeb) return;
	clearInterval(a);
	for (var i = 0, l = _loadCallbacks.length; i < l; i++) { _loadCallbacks[i].apply(window); }
}, 25);

function xojoReady(fn){
	_loadCallbacks.push(fn);
}

//Your code here
//xojoReady(fn) is similar to jQ Ready but it is executed when XojoWeb is loaded.

xojoReady(function(){

XojoWeb.sendDataToXojo = function(data){
	o = new XojoWeb.JSONItem(); o.set("message", "json-data"); o.set("name", "json-data"); o.set("stack", JSON.stringify(data));
	XojoWeb.session.comm.triggerServerEvent("Event","JavaScriptError", o);

}

});

</script>

This script will do two things it will add the following functions:

XojoWeb.sendDataToXojo(dataObj) and a XojoReady(fn)

XojoWeb.sendDataToXojo you can use from anywhere that can access XojoWeb. For example:

var data = { name: "Jose", salary: 200 };
XojoWeb.sendDataToXojo(data);

The XojoReady() is just a little helper function to execute callbacks when XojoWeb is available.

The htmlViewer is a bit of different story if you don’t have a reference to XojoWeb but I haven’t used the htmlViewer to see how is currently implemented.

For iframe the common trick to talk to the parent is to use window.parent.postMessage() and window.addEventListener("message") in your main page to handle the message.

Hope it helps…

I took a look at the htmlViewer. When you use the html property it is implemented as a basic div. When using a url it is an iframe in which case the XojoWeb is not accesible regularly.

In that case here is a revised script to inject using the header property of the WebApplication in the inspector.

<script>
var _loadCallbacks = [];
var a = setInterval(function(){
	if(!window.XojoWeb) return;
	clearInterval(a);
	for (var i = 0, l = _loadCallbacks.length; i < l; i++) { _loadCallbacks[i].apply(window); }
}, 25);

function xojoReady(fn){
	_loadCallbacks.push(fn);
}

//Your code here
//xojoReady(fn) is similar to jQ Ready but it is executed when XojoWeb is loaded.

xojoReady(function(){

XojoWeb.sendDataToXojo = function(data){
	o = new XojoWeb.JSONItem(); o.set("message", "json-data"); o.set("name", "json-data"); o.set("stack", JSON.stringify(data));
	XojoWeb.session.comm.triggerServerEvent("Event","JavaScriptError", o);

};

window.addEventListener('message', function(e) {
	console.log("@message %o", e);
	if(!e || !e.data || !e.data.cmd) return;
	if(e.data.cmd == "xojo-send-data"){ XojoWeb.sendDataToXojo(e.data.data); return; }

}, false);

})
</script>

With this code now you can do the following inside an iframe:

window.parent.postMessage({cmd:"xojo-send-data", data: {"name":"Jose Cuevas"} });

That will tell the parent to call XojoWeb.sendDataToXojo()

Don’t do this. None of the JavaScript portion of the web framework is documented yet and anything that’s not is subject to change without notice.

Once the WebSDK docs are out, those API items will be frozen, but it will definitely not include anything under XojoWeb.session.

I said this about Web 1 and I’m saying it again about Web 2. I know that there are a lot of experienced web developers using Web 2, but please, keep in mind that the users you are giving solutions to are sometimes not and will just copy/paste the code you give them and move on without any understanding of what you are telling them to do.

Arbitrarily calling methods deep in the bowels of the web framework is a great way to shoot yourself (and others) in the feet because we have lots of plans, some of which we already know are going to drastically alter things. Things that I’ve seen some users calling in examples here on the forum (like the one I called out above).

2 Likes

Completely understood.

Hope people understand these are very fragile workarounds that can only be used with complete understanding of their context and implications.

Well, to be clear, the methods I’m sharing will likely be the documented and supported methods when that documentation becomes available as it’s the intended API, at least for the currently available version of the Web 2.0 SDK.

2 Likes

How about in WEB1?

This is what needs better documentation . . . The triggerserverevent.

I took another crack at it, this time avoiding using any secret sauce (internals) that may break easy with a future releases.

https://drive.google.com/file/d/1S1H0m1TcFRrjsKS9wgQv5SWXbbDzs21b/view?usp=sharing

It just uses a plain webTextField and its valueChanged event to pass JSON back to Xojo.

All we need is to add a textfield that’s not visible, set its super to the new subclass and handle the recieveMessage event (a new event added in the subclass).

Its all wrapped up and self-contained in a webtextfield subclass.

In Javascript it adds the global function xojoSendMessage() for example:

xojoSendMessage("myMessageName", {"name":"Jose Cuevas"} );
xojoSendMessage("myMessageName", {"name":"Jose Cuevas"}, "myScopeTag" );

Just another option for anybody that wants to push basic messages with data to Xojo.

For Web 1.0 I made another version, the scopeTag is not really implemented, and actually doing this in Web 1.0 was way easier. Since in Web 1.0 we can reference controls by their name maybe the scopeTag could be replaced with the actual name of the textfield.

https://drive.google.com/file/d/1AZrEIQ3CnR8ArXJTJNJ87BmaFftTGWFp/view?usp=sharing

In web 1, look at the WebSDK docs in the Extras folder.

1 Like