JavascriptComponent integration difficulties

Hi,
I am trying to integrate a javascript Spreadsheet component with CUBA and I’m experiencing the following difficulty. In the client side of the component, I define a custom spreadsheet formula which receives some arguments and calls a method from the middleware to process the data.

this.addCustomFormula("SUM", function (args) {
    var excelObj = $('#Spreadsheet').data("ejSpreadsheet"),
    argument = excelObj.getValueFromFormulaArg(args);
    connector.netChange(argument); //<-- Middleware method
    var state = connector.getState();
    var data = state.data.result;
    return data;
});

The method that is called in the middleware is the following one:

jSpreadsheet.addFunction("netChange",callbackEvent->{
   JsonArray arguments = callbackEvent.getArguments();
   JsonObject jsonObject = arguments.get(0);
   double arg1 = jsonObject.getNumber("arg1");
   double arg2 = jsonObject.getNumber("arg2");
   double arg3 = jsonObject.getNumber("arg3");
   SpreadsheetState state = new SpreadsheetState();
   state.result = CalcAccountAmt(arg1,arg2,arg3);
   jSpreadsheet.setState(state);
 });

My intention in the client side method is to send the arguments in the middleware, get the calculated value synchronously and return this new value back to the spreadsheet. The problem has to do with the fact that the communication model between client & server components is asynchronous so I cannot get directly the new value from the State object. The new value is available through onStateChange() function but in my case this is not very useful since I need the new value before my javascript method returns. Is there any workaround for this kind of situations?

Hello @papageor

I suggest you to use RPC calls mechanism.

Regards,
Daniil

Hi Daniil,
Thank you for your reply. I just wish to ask you if there is any available sample either from cuba-platform or any add-on in the github that I could you as a starting point.

Thanks

Hi @papageor!

JavaScriptComponent has the following features which can serve your needs:

  1. Register a function that can be called from the JavaScript using the provided name.
  2. Invoke a named function that the connector JavaScript has added to the JavaScript connector wrapper object.

Putting these together, we can have the following:

Controller

// Register a function that will be called from the client-side. 
jsComponent.addFunction("valueChanged", callbackEvent -> {
    JsonArray arguments = callbackEvent.getArguments();
    String text = arguments.getString(0);
    
    // Send back the updated value
    jsComponent.callFunction("updateValue", text.toUpperCase());
});

JS Connector

com_company_demo_JsDemo = function () {
    var connector = this;
    var element = connector.getElement();
    element.innerHTML =
        "<div class='caption'>Hello, world!</div>" +
        "<div class='textinput'>Enter a value: " +
        "<input type='text' name='value'/>" +
        "<button name='btn'>Click</button>" +
        "</div>";

    var input = element.getElementsByTagName("input")[0];
    // Update the value from the server-side 
    connector.updateValue = function (text) {
        input.value = text;
    };

    var button = element.getElementsByTagName("button")[0];
    // Send the value to the server-side
    button.onclick = function () {
        connector.valueChanged(input.value);
    };
};

Demo project: js-demo.zip (77.0 KB)

Regards,
Gleb

Hi Gleb,
Thank you very much for your prompt reply - it is really very interesting this approach but I think that there is a detail in my approach that requires a different approach. As you will notice in the javascript component (SpreadSheet), a javascript function is called from the spreadsheet and passes the arguments to a middleware method. The middleware method should process the data and returns back synchronously the result - this result is finally sent back from the javascript method to the component in order for an appropriate cell to get updated. At the provided example, I see that there are two separate steps involving two different actions - calling first a middleware method that it will call in turn another function. So, I was thinking of using a REST method call or RPC calls - I’ve checked the provided link from Daanil but I am not quite sure about how to use this mechanism correctly in the Cuba application.

Thank you

This is impossible in modern web browsers. Synchronous AJAX calls are deprecated and are not recommended to use in web applications since they freeze the entire browser tab.

Thank you all for your valuable feedback. Although my request seems to be impossible for technical reasons, I’ve found your answers very useful!

Regards,
George

Hello, I am having an issue using the controller / connector setup you posted. I downloaded your js-demo to test what I was doing wrong in comparison to my current project. Your file worked without issues, until I renamed the function name in both the XML and the JS files. When I ran it, it gave me an error:

“Could not initialize JavaScriptConnector because no JavaScript function was found. Make sure one of these functions are defined:
com_company_demo_JsDemo_edited”

I found it odd that it didn’t work considering I changed the name of the function, so I changed it back to it original name and it gave me the same error

“Could not initialize JavaScriptConnector because no JavaScript function was found. Make sure one of these functions are defined:
com_company_demo_JsDemo”
image
image
image

Hi,

In simple case it’s enough to refresh the page with cache disabled. To do so:

  1. Open developer tools
  2. Check Disable cache
  3. Refresh the page while developer tools is still opened

09

Regards,
Gleb

2 Likes

thank you, that solves it!