Le’ts say we have some business logic shared among dozens of datamappers and templates.
How could we proceed to avoid embedding such scripts in each datamapper\template?
The ideal approach would be to define scripts in a shared directory and have the datamapper and template load them at runtime.
A practical example is a REST API that the Designer have to query to retrieve data to use in the datamapper and the template.
If for any reason we change the API schema we would have to manually go in each datamapper and template to reflect the changes, on the other hand, if we have the logic in files outside the connect resources we can apply the update in one single point.
On the other hand defining a REST client outside the scope of Designer could allow defining it once and for all.
I typically use a Workflow/Automate process to act as an proxy to retrieve images and content that are behind protected endpoints in CMS/ECM systems. So my scripts in the templates are agnostic to the environment and all use the same endpoint (and credentials) setup in the Workflow/Automate process. It is the process/flow that retrieves the respective content.
For script files this is currently handled through eval() as explained in the post, which you already found:
When in need in handling an arbitrary number of files I would combine these files using a tool like Minify in Visual Studio Code. This VSC extension can minify js (and css) files but is also able to combine all files in a folder (leaving your original files untouched).
I’ll bump the remote Control Script ticket on our backlog.
Thanks for the feedback! Minifying and merging all the JavaScript snippets could be viable so that we can use a single loadtext eval() in each template.
Anyway as mentioned in the other thread this method applies only to templates, in the datamapper we don’t have a load eval function.
For future reference, as suggested we also added a datamapper runtime property APIEndpoint which is passed down by the call to the datamapper so that the HTTPRequest object is created with a variable instead of an hard coded URI.
JavaScript’s native eval() method is available in the DataMapper.
You can try it with the following code:
var command = "logger.warn('Hello World!')";
eval(command)
Note that using eval() can be risky: if you don’t have full control over the content of the code it evaluates, it could wreak havoc on your system. Use at your own risk.
let file = openTextReader('E:/scripts/global.js')
let f = ''
while( (line = file.readLine()) != null ) {
f += line
}
logger.info(f)
eval(f)
The logger shows that the whole file have been assembled in the f variable correctly bu the eval function while not throwing any error seems to no execute anything. Functions defined in global.js aren’t available.
Of course if I copy paste the content of the script inside the script panel of the Datamapper everything works, so I would exclude any issue on the script side.
That’s one of the peculiarities of JavaScript we all love/hate…
The eval() runs in its own local scope (not actually local, but for the sake of this discussion, let’s assume it is). To ensure all functions declared in the JS file you load are available globally, you need to make the eval() method itself global. Fortunately, that’s easy to do. Change your code to:
var myEval = eval;
let file = openTextReader('E:/scripts/global.js')
let f = ''
while( (line = file.readLine()) != null ) {
f += line
}
logger.info(f)
myEval(f)
Well that’s a thing I didn’t knew about eval in general.
However, it still doesn’t work and functions declared in the global.js file are not found. Might be there any other peculiarities?
The golbal scripts declares functions as classic js:
function myFunc() {
...
return ...
}
Nevermind, I just noticed that assembling the read file like f += line made f as one-liner, as such a comment before the function declaration actually resulted in commenting the whole thing.
Solved by preserving new lines with f += line + '\n'.