Custom tables in Personas (take 2)
A little over a year and a half ago I wrote a blog about Creating a custom table in Personas using HTML tables, because Personas didn’t support adding tables as a properly supported custom object type. That was in the days of Personas 2. Now Personas 3 is here and there still isn’t a custom table object, but we can now do better than the HTML tables described in that blog. Personas 3 scripting is much improved and SAPUI5 has a really nice table type. I previously wrote about Using SAPUI5 charts in Personas Flavours and prompted by Gideon van Zyl‘s request here is how you add UI5 tables to a Personas flavour.
I’m going to use the same transaction – the Personas 3 migration tool – as I used for the chart example, and I’m building a UI5 table to replace the existing table in that transaction – the same one I built a chart for. The code to read the data from that chart into a JavaScript array is therefore identical, and a straight copy/paste from this wiki page – Copying Table data into a variable. This time, I don’t need to filter or summarise the data at all so I’ll build the table directly on the “contents” array produced by that code, which is structured like this:
[ {FLAVOR_ID: "9C9FD354F303452BE100000089CDA76F", FLAVOR_NAME: "temp", OWNER: "MASAD", APP_ID: "IW26", STATUS: "IGNORED", …}, {FLAVOR_ID: "9894FC5286F14271E100000089CDA76F", FLAVOR_NAME: "Department Heads", OWNER: "MASAD", APP_ID: "SE38", STATUS: "PROCESS", …}, {FLAVOR_ID: "2D070F53DF372B33E100000089CDA76F", FLAVOR_NAME: "SAP Release", OWNER: "MASAD", APP_ID: "ME23N", STATUS: "PROCESS", …}, {FLAVOR_ID: "7ED8845427A5AC64E100000089CDA770", FLAVOR_NAME: "asug demo", OWNER: "MASAD", APP_ID: "KS03", STATUS: "PROCESS", …}, {FLAVOR_ID: "F1ADB9542D034F23E100000089CDA76F", FLAVOR_NAME: "temp", OWNER: "MASAD", APP_ID: "SM50", STATUS: "PROCESS", …}, .... ]
As before we’re going to build a string of HTML to send to an HTMLViewer control, and we need to start with the usual boilerplate to load the UI5 libraries:
var tableHTML = '<html> <head<\ <script src="/ui5/1/resources/sap-ui-core.js" \ id="sap-ui-bootstrap" \ data-sap-ui-libs="sap.ui.core,sap.ui.commons,sap.ui.table" \ data-sap-ui-theme="sap_goldreflection"> </script> \ <script>';
Now we build the table object and add the columns we need – we don’t need to display in the table all of the columns in the “contents” array, and in fact I don’t here:
tableHTML += '\ var oTable2 = new sap.ui.table.Table({ \ title: "Personas 2 migration status", \ visibleRowCount: 15, \ columnHeaderHeight: 30, \ selectionMode: sap.ui.table.SelectionMode.Single, \ }); \ \ oTable2.addColumn(new sap.ui.table.Column({ \ label: new sap.ui.commons.Label({text: "Flavor ID"}), \ template: new sap.ui.commons.TextView().bindProperty("text", "FLAVOR_ID"), \ sortProperty: "FLAVOR_ID", \ filterProperty: "FLAVOR_ID", \ width: "200px" \ })); \ oTable2.addColumn(new sap.ui.table.Column({ \ label: new sap.ui.commons.Label({text: "Flavor Name"}), \ template: new sap.ui.commons.TextField().bindProperty("value", "FLAVOR_NAME"), \ sortProperty: "FLAVOR_NAME", \ filterProperty: "FLAVOR_NAME", \ width: "200px" \ })); \ oTable2.addColumn(new sap.ui.table.Column({ \ label: new sap.ui.commons.Label({text: "Owner"}), \ template: new sap.ui.commons.TextField().bindProperty("value", "OWNER"), \ sortProperty: "OWNER", \ filterProperty: "OWNER", \ width: "200px" \ })); \ oTable2.addColumn(new sap.ui.table.Column({ \ label: new sap.ui.commons.Label({text: "Transaction"}), \ template: new sap.ui.commons.TextField().bindProperty("value", "APP_ID"), \ sortProperty: "APP_ID", \ filterProperty: "APP_ID", \ width: "200px" \ })); \ oTable2.addColumn(new sap.ui.table.Column({ \ label: new sap.ui.commons.Label({text: "Status"}), \ template: new sap.ui.commons.TextField().bindProperty("value", "STATUS"), \ sortProperty: "STATUS", \ filterProperty: "STATUS", \ width: "200px" \ })); \ ';
All of the columns here are simple text fields, but you can of course use any of the available types – checkboxes, etc – where appropriate.
Next we create a data model and bind the table to it:
//Create a model and bind the table rows to this model tableHTML += 'var oModel2 = new sap.ui.model.json.JSONModel(); \ oModel2.setData({modelData: ' + JSON.stringify(contents) + '}); \ oTable2.setModel(oModel2); \ oTable2.bindRows("/modelData"); \ ';
Finally, we place the table control on the page and finish off with the usual HTML, and of course send all this HTML to the HTMLViewer control:
tableHTML += 'oTable2.placeAt("content");'; tableHTML += '</script>'; tableHTML += '</head>'; tableHTML += '<body class="sapUiBody" supportedthemes="sap_corbu" role="application">'; tableHTML += '<div id="content"> </div>' tableHTML += '</body></html>'; session.findById("wnd[0]/usr/htmlViewerPersonas_1447866610072").content = tableHTML;
After editing the screen to remove the original table and add a suitably sized HTMLViewer control and running this script, you end up with a screen like this:
This gives you the usual UI5 sorting and filtering capabilities when clicking on the column headings. And, of course, UI5 tables have many more capabilities than this, including fixed initial columns, different column types, and much more. This example is placing a table on the same screen as the data, and in this case the original data is in a nice table already so there’s not a lot of point. There’s no reason, though, why you can’t create the UI5 table on a different screen. That’s exactly the scenario I originally wanted in my “HTML tables” blog – copying a table from one screen to another. This is a much nicer looking solution for that scenario.
For the moment this table is “read only”. There’s no way to interact with it to enter or change data, or to trigger events when rows or cells are selected. I think at least some sort of interaction is possible but I haven’t got it working yet. A blog will follow in due course if/when I make it work!
Hi Steve,
This is awesome!Thanks!!
Kind Regards
Deon
Very useful, thank-you. Have you made any progress on having the HTML Viewer contents interact with the rest of the Persona elements?
I'm currently trying to essentially create a master-detail control between the WBS Elements and their associated Milestones within the CJ02 transaction - i.e. on the Milestone screen you can see a list of WBSs populated from the previous screen and when you select one, it performs a refresh of the Milestone data for the new WBS.
While I can fetch the list of WBSs and refresh the Milestones, I haven't yet been able to join the two such that a selection made from the list triggers the refresh. With the list built in a HTML viewer, the problem seems to be that the HTML viewer's iFrame can't access the parent document due to security controls around cross-site scripting (I did find some stackoverflow solutions, but apparently they don't work if the container object is located on your localhost, which is true here).
Of course, I'm only trying this because it doesn't seem possible at the moment to dynamically change the values in a standard Personas ComboBox/Dropdown object.
I'm pretty new to both Personas and Javascript though, so it's quite possible I've missed some tricks while trying to cobble together a solution from articles on here and elsewhere 🙂 .
It's okay, I've got it working now. The document.domain in the HTML Viewer frame needs to match that of the parent frame in order to access elements in the parent frame and I just wasn't setting it properly. So this is an example of what can work - a script to populate HTML Viewer content which includes its own <script> section. This script within the HTML content:
var lc_html = "";
lc_html += "<script>";
lc_html += "document.domain = 'mydomain.co.uk';";
// where mydomain.co.uk is shared with the parent Personas iFrame
lc_html += "function myFuncSel(elementID) { ";
lc_html += "var lc_field = parent.document.getElementById('wnd[0]/usr/txtPersonas_1461245787519');";
lc_html += "lc_field.value = elementID;";
lc_html += "} ";
lc_html += "</script>";
// Some other stuff would follow such as building a table within the lc_html variable...
// ... but ultimately we set the HTML Viewer control content to lc_html.
session.findById("wnd[0]/usr/htmlViewerPersonas_1461342337603").content = lc_html;
Yes, the document.domain trick was what I was missing for a while. Well done.
I've had to take a little break from this stuff for a while, but I hope to get back to it soon. There's still lots of potential, I think, in marking the worlds of SAPgui and UI5. Have you seen this blog by Vigneshkkar Ravichandran: Interactive Charts in Personas 3.0?
Steve.
I hadn't seen the blog, but I do want to try putting charts into a page as soon as I can find a requirement that justifies it, so will take a look. Thanks 🙂 . I also need to look at UI5 for some new developments we've got in the pipeline.
Also, for the benefit of anyone else trying to get HTML Viewer objects to interact with its Personas parent, worth noting that while setting the .value of a Personas object fetched getElementById will display that value correctly on-screen, this value will not be returned by session.findById("personasobject").text. To ensure they match, you have to explicitly set:
session.findById("personasobject").text = document.getElementById("personasobject").value;
But as far as I know you can't do this within the HTML Viewer content, because that can't use the 'session.findById' function.
So, in the context of my example where the HTML Viewer holds a master data list and the detail sits in the parent Persona, the user currently has to select an entry in the list (which uses a Javascript onchange function within the HTML content to set the .value of a Persona text object), then press a 'go' button (which calls a script to set the Persona text object .text equal to the .value and fires a refresh of the details for the chosen entry).
Hi,
nice work! If you want to call and manipulate the embedding Dynpro screen from within your html viewer script/page them you can use sap.personas.scripting.executeScriptInternal(). It takes an object as parameter with a property src containing a string of the script you want to execute. The advantage is that you get access to the session and after the script finishes the screen is updated (with all the respective onLoad and onAfterRefresh lifecycle event handling).
The downsides:
1) This is only available in the WebGui
2) It can be quite daunting to write the script string to be executed. It's a string within a string, within a string....
3) The current implementation of the WebGui does not retain the state of the html viewer when the embedding page is refreshed. This is no problem for simple html pages, but in your table scenario, your table scroll position, row selections, filters, reordered columns are lost. This may be fixed in future kernel releases.
Sample:
var html = "<html><body>";
html +="<script type=\"text/javascript\">";
html +="document.domain = \"sap.corp\";\n"; // use your domain!
html +="var sScript='session.findById(\"wnd[0]/titl\").text=\"'+new Date()+'\";';\n"
html +="function doIt(){\nparent.sap.personas.scripting.executeScriptInternal({src:sScript});\n}";
html +="</script>";
html +="<button onClick=\"doIt()\">Go</button><br/>";
html+="</body></html>";
Hope this helps,
Clemens
Hi Steve,
This is extremely helpful. Scroll bars, sorting everything can be achieved. The UI5 table is working fine standalone. But I am getting 'Flavor contains non-whitelist URL' error while passing content to HTML viewer. I added (http|https):\/\/.* in whitelisting activity.
Still i am getting the same error. Can you help?
Regards,
Gaurav
Add "/.*" to the URL whitelist table to make the HTMLViewer work.
Steve.
I added /.* and now HTML Viewer is working fine and i am getting no errors. But still I am not able to see any output for above method. I checked my code in Eclipse editor on UI5 workbench and it is working fine. Just it won't print using HTML Viewer in Personas editor.
Can it be any SAP note issue??
Gaurav
Found the issue..UI5 core library was missing. Caught in debugger.
Hi,
I have followed the same steps. But when i tried to assign the content to htmlviewer in the personas.Content is not getting populated in the html viewer. I have done this using the statement "session.findById("wnd[0]/usr/tabsTS_ITOV/tabpTCMA/ssubSUBPAGE:SAPLCSDI:0152/htmlViewerPersonas_1476973371183").content = tableHTML; ".
We are using SAP screen personas 3.0 SP2.
Can you please help.
Regards,
Hari
Hi,
Just a thought but the reference to the HTML Viewer seems wrong?
When I post content to my HTML viewer the statement is this:
session.findById("wnd[0]/usr/htmlViewerPersonas_147582619219979").content = tableHTML;
The statement you are using seems to reference a SAP reference (in bold below) as well as your html viewer?
session.findById(“wnd[0]/usr/tabsTS_ITOV/tabpTCMA/ssubSUBPAGE:SAPLCSDI:0152/htmlViewerPersonas_1476973371183”).content = tableHTML; “.
regards,
Ian
Hi,
Thanks.

I have tried using the statement "session.findById("wnd[0]/usr/htmlViewerPersonas_1476973371183).content = tableHTML; " as well. But no luck so far. When I check it in debugging mode, it is specifying that content parameter is not available in session.findById("wnd[0]/usr/htmlViewerPersonas_1476973371183). Below is the screenshot for your reference.
Thanks.
Regards,
Hari
Hi Steve,
Thank you for this useful blog.
As you said " For the moment this table is “read only”. There’s no way to interact with it to enter or change data, or to trigger events when rows or cells are selected. I think at least some sort of interaction is possible but I haven’t got it working yet. A blog will follow in due course if/when I make it work!"
So is there any update on this? Have you found something to make it editable and trigger events on row or cells selection?
Regards,
Parul
Hi Steve,
I have tried above scenario. I called RFC function module to get the delivery, delivery date and tracking number and stored in contents.
Hi Steve,
Thank you for your valuable info of creating a custom table in Personas. I tried creating a HTML viewer table and got the custom data successfully.
Now I had an other challenge getting the data from the HTML viewer. I have tried using the statement "var tc = session.findById("wnd[0]/usr/htmlViewerPersonas_160091915563491");". But no luck so far. When I check it in debugging mode, I couldn't able to get the HTMLViewer data. Below is the screenshots attached for your reference.
Is there any other way to read the data from HTMLVieiwer Table ?
Thanks much in advance.
Regards,
Venkata
You set the HTML viewer content by assigning to the "content" component, so my first guess would be to try reading from there, so rather than your current:
try
Thank you Steve.