Data caching in Personas scripts
Personas scripting has actions for copying data from SAPgui screens and pasting it back elsewhere. This is very commonly used to copy data between screens, or even between transactions. That basic technique is described here – SAP Screen Personas – How to fetch data from another screen. Once the script has finished executing, though, the values copied disappear. It isn’t possible for another script to reference those values – each script execution starts in a completely clean environment. One work around for this is to paste values into hidden fields on a screen, from where other scripts can copy them again. This works well for small numbers of values. What if you have a lot of values, or if you don’t know how many there will be until you run the transaction? In such cases this technique becomes unwieldy, or even impossible.
This second case, not knowing how many values there are, is exactly the situation I encountered when trying to build a custom table on the SAP home screen. Personas doesn’t have a native custom table object and so I had to build a fake table out of unrelated text field objects and then manage the scrolling myself. The data from the table came from a simple report and obviously I didn’t know until I ran the report how many rows it would return. Consequently it just wasn’t possible to hold the whole table contents in hidden fields on my home screen. That meant that each time I scrolled the table I had to re-run the report and copy out the relevant rows. The details of this process are described here – Creating a custom table in Personas.
Personas scripts have a limited number of standard actions but you can use Javascript within a Personas script to do many things that these standard actions do not provide. The scrolling for my custom table was implemented largely in Javascript. Within a piece of Javascript inside a Personas script, you can reference variables created by Copy Value or Copy Table actions. For example, once I’ve run my report, I use Copy Table to grab the data from the report into an object called “po_list” and then within Javascript I can reference this table, now a Javascript array, as “args.po_list”:
Now this “args” object is exactly the object that’s cleaned out at the end of a script execution, and so even in Javascript you can’t reference values copied by another script. Notice, though, that as well as referencing values via this args object I also reference standard Javascript objects – rows and cols in the above example. I do this mostly to save typing – if the values aren’t needed for pasting into screen field later they don’t need to live inside the args object and I can save 5 keystrokes for each object reference. Programmers are lazy! But that got me thinking – are those values cleared out at the end of a script execution like the values in the args object? It turns out they are not. That means that from within a Javascript action I can save values that will persist and be visible to Javascript actions in other scripts no matter when those scripts are executed. The only thing that seems to delete such values is a browser refresh. Could I use this technique to build a cache behind my table? Then I’d need to run the report just once and subsequent scrolling will be done via the cache without the need to re-run the report.
In version 1 of the custom table, I had Javascript code like this after the Copy Table action:
rows = args.po_list[0].length; args.rows = rows; first = parseInt(args.first); if(rows-first > 1) { args.ponum1 = args.po_list[0][first+1]; args.vendor1 = args.po_list[1][first+1]; args.date1 = args.po_list[3][first+1]; args.relcode1 = args.po_list[4][first+1]; } if(rows-first > 2) { args.ponum2 = args.po_list[0][first+2]; args.vendor2 = args.po_list[1][first+2]; args.date2 = args.po_list[3][first+2]; args.relcode2 = args.po_list[4][first+2]; } ...
What I can do instead is copy that “args.po_list” table to a “po_list” table so it doesn’t go away, like this:
cols = args.po_list.length; rows = args.po_list[0].length; po_list = new Array(); for(i =0; i < cols; i++) { po_list[i] = new Array(); for(j = 0; j < rows; j++) { po_list[i][j] = args.po_list[i][j]; } }
This takes the “args.po_list” object created by the Copy Table and does a deep copy of it to an object called “po_list”. A deep copy is needed because in Javascript a 2-dimensional array is actually a one dimensional array of one dimensional arrays. A simple shallow copy would leave us still with references to the args object, and they would disappear when the script execution finished. Then I can access the table like this (just losing the “args.” references):
rows = po_list[0].length; args.rows = rows-1; first = parseInt(args.skip); if(rows-first > 1) { args.ponum1 = po_list[0][first+1]; args.vendor1 = po_list[1][first+1]; args.date1 = po_list[3][first+1]; args.relcode1 = po_list[4][first+1]; } if(rows-first > 2) { args.ponum2 = po_list[0][first+2]; args.vendor2 = po_list[1][first+2]; args.date2 = po_list[3][first+2]; args.relcode2 = po_list[4][first+2]; } ...
Finally I need to only run the report and copy the data if my “po_list” cached copy is empty.
Step 2 decides if the cache is empty or not, based on the number of entries in the array, and steps 3-9 run the report and cache the data only if necessary. Step 8 here is the deep copy process I described above.
You can use this technique to save data for reuse within a flavor as I do here. You can also use it to save data where it can be referenced by scripts in other flavors on other transactions. I’m sure there’s a lot of potential here that waiting for somebody with suitable imagination to discover. If you have any suggestions, please do add them in the comments below!
It is really interesting to see how much you can achieve by combining Javascript with standard Personas scripting actions. Much more than I realised when I started my Personas journey.
Disclaimer
This is not an officially supported technique. I discovered it by accident and I can’t promise it will continue to work. I’d be surprised if future patches for Personas v1 broke it but when the next major version comes along there’s no telling what will happen to this technique. It may well not work. If you invest a lot of effort in building flavors that use this feature, you do so at your own risk!
Steve, you really are "The Man" when it comes to Personas.
I agree with Tim, Steve is "the Personas" man 🙂
Steve, is there any way to bring the value of a text field from one flavor to another by javascript instead of recording.
I believe the technique described here, of storing data in the browser's global window object, allows that data to persist for as long as the browser window isn't refreshed. You can use it to pass data between flavours of the same transaction, or indeed to flavours of different transactions.
Be aware that in Personas 3 flavours will be usable in SAPgui (Windows and Java versions) as well as in the browser, and this technique will work only in the browser - there is no global window object accessible to the scripting engine in the GUIs. There may be a different mechanism for storing values globally but it won't be this. Until it is clear how that mechanism will work, if it exists at all, I would be wary of building too much that relies on it.
Steve.
Hi Steve,
I have checked it in Personas 2.0 , but it is not working.
Is there a way to use IF Condition for the Javascript variables in Scripting.
Thank,
Arunkumaran
Hi Steve,
Great blog much appreciated, but as always with me I have a question in relation to this. Being a total javascript novice I wonder if you could help me. My scenario is to copy a value, and store this using javascript that I can then call upon this value to paste it later on in the same transaction, but after loads more actions have been performed.
Do I just reference the copy value say: 'name' then in my Calculate in Javascript have the following: args.namestore = args.name
Then later on in another script can I then just Paste Value 'name'?
Have I interpreted this correctly?
Many thanks Steve,
These "cached" values are only accessible from within a "Calculate in Javascript" action. Before you can paste them into a screen field you need to copy them to an "args." object. So something like:
On screen 1:
Copy Value to "value"
Calculate in Javascript: savedValue = args.value
...lots of stuff in between...
On screen 2 (or back on screen 1, I suppose!):
Calculate in JavaScript: args.value = savedValue
Paste Value from "value"
Does that make sense?
Steve.
It made perfect sense to me, have tried it and it works as I wanted it to work.
Once again, you are the man.
Thanks Steve
🙂
Hi Steve,
I tried simulating the Copy Table and taking the total elements using the below javascript.
rows = po_list[0].length;
Javascript returns only 51 (including header) as the maximum count. Whereas, if I export the same output to excel file, I observed more than 51 records.
I would like to know,
1. why the count is restricted with 51 though there are more than 50 records?
2. How do I get the same count of Excel?
Thanks,
Panchpeer
I believe the Personas 2 client side fetches only 50 rows at a time from the backend table - row 51 is the header row, I think. To get more you have to scroll the table. I'm sure I remember reading an article about how to manage this, but I can't find it now. Maybe Sushant Priyadarshi can help?
Steve.
Thanks Steve.
Hi Sushant - Could you please help?