xTuple.com xTupleU Blog & News Customer Support

toolbox.loadUi

OK,

I am using xTuple 5.0

Here is what I got so far.

I added a script named crmaccount.js to add tabs to the xTuple client Account screen. This is successful.

Next I created a screen that display data from a custom table and is linked to the crmacct_id

This screen works at stand alone

So I want to insert that new screen into the tab created above.
I use the code

var se_list = toolbox.loadUi(“employer_ins_screen”,insurancePanelPage)
se_list.objectName = “emp_ins_list”;
var se_list_params = new Object
se_list_params.crmacct_id = mywidget.id();

This DOES display the new screen in the tab that I created.

But the code behind the new screen does not run, so I added the code

include(“employer_ins_screen”);

Then the code DOES run and the data in the custom table for the specific crmacct_id is displayed.

BUT, it only is displayed when running the DEBUGGER
Normal running will not display any data.

So I figured that I have a timing problem, the code is not loaded into memory

Is there a solution here or am I going about this wrong?

Thank you

Bob

I typically use something like the showEvent() function to trigger scripted events. showEvent() is supposedly called after the screen is displayed so may resolve your timing issues.

The biggest issue with functions like showEvent() (and set()) is if there are more than one script on the screen calling that function. The script engine only calls those functions once not for all script instances. There are ways to code around that but let me know if that might be a problem and I can provide some examples…

I will give that a try Anderson.

Do you know how xTuple adds these scripts to the core code?
Are they added as a separate class for each script?

My concern is that I will have many scripts added to the Account screen due to the fact that we are adding a lot of custom code.

In the xTuple script ediProfile, they use a function() wrapper
and declare functions like xtConnect.ediProfile.save = function()

is this the recommended practice?

rekosko,
Im sure Anderson will reply with a little bit better detail, But I think I may can shed some light.

How does Xtuple add scripts to core code:

For one the scripts are not added to the “core code” they are run-time executed java script. The scripts have an order code by which they are executed in case you make 2 scripts and you want to run one first. But they are not compiled. They simply modify the running code. There are not a separate classes for each script, however if you use a naming convention inside each of script like “xtConnect.ediProfile.save = function()” it allows you to name methods common things such as “save” without overriding other important code.

I would say if you plan on adding multiple scripts and especially if you are going to “include” multiple scripts then you will probably want to give each script an object to reference so that it appears as a class with public variables. similar to that of the xtConnect object reference before.

Bob,

The details of when scripts run are messy (long story). Many of the bugs in 4.11.0 - 4.11.2 were caused by changing the timing of script execution so we’re reluctant to change it again. Suffice it to say that by the time a window or widget is visible or its set() function has been called, all of its scripts have been run.

Scripts are loaded in a specific order:

  • inheritance hierarchy (e.g. display will be run before dspWoSchedule)
  • script_order (e.g. dspWoSchedule order 5 will be run before dspWoSchedule order 10)
  • schema name (e.g. display scripts with order 0 in xtbatch before xtdash)

As you learned empirically scripts don’t run automatically when you call toolbox.loadUi(). Calling include() is a reasonable solution, as is putting the code inline.

Dave Anderson’s suggestion to use showEvent() for populating your custom table is probably the safest route to go to make sure stuff happens before the user sees it. As Dave noted, there are problems when you have multiple scripts on the same widget, particularly related to variables (and functions in JavaScript are variables) with the same names. There are three solutions to that problem and you should use at least two of them:

  • Always wrap calls to set() and showEvent()
  • Either create namespaces (like the example you cite above in xTuple Connect)
  • and/or write closures & immediately executed function expressions (IIFE)

Depending on the age of the xTuple code you look at, you’ll see all three. The oldest code ignores the problems entirely.

The following example represents my current recommendations. There are differing opinions on the best way to handle set() and closeEvent(); the version here is the simpler of them.

// "display" script
// add an Expand All pushbutton that only works the first time you click it
var self = this;                  // get the runtime context set up by prior events
(function () {                    // create a "function expression"
  var _expand = new QPushButton(qsTr("Expand All"), mywindow),
      _list   = mywindow.list(),  // _list is only visible inside the function expression
      _lyt    = toolbox.widgetGetLayout(_list);
  _lyt.addWidget(_expand, 3, 0);  // row 3 works for most displays

  function expandAll()
  {
    if (_list && "expandAll" in _list) // if _list is valid
      _list.expandAll();          // show all indented items in _list
    _list = false;                // make _list invalid for demonstration purposes, any value would do
  }                               // but this only changes `_list` inside the function expression

  _expand.clicked.connect(expandAll);

  // if a set() function already exists, save it. otherwise save a dummy version
  var oldSet = (typeof self.set == "function") ? self.set : function () { return mainwindow.NoError; };
  self.set = function (pParams)   // NOT function set(pParams) { ... }
  {                               // replace the prior set() with a new one
    var result = oldSet(pParams); // call the function we just saved
    if (result != mainwindow.NoError)
      return result;              // return if an error occurred
    print("pParams", JSON.stringify(pParams)); // do whatever this `set` should do
    return mainwindow.NoError;
  }
})();                             // execute (run) the function expression

Think carefully before you write multiple scripts on the same window, even if you code them well. The more scripts there are, the harder it is to understand what’s really going on and the more code duplication there will be.

If you’re going to create a lot of include scripts then I agree with Caleb — each included script should create an object that can be treated like a class, or at least a class instance.

Gil

thank you Gil and Caleb and Anderson,

I will start using the class reference as in xConnect