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:

Screen Shot 2015-11-26 at 14.34.57.png

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!

To report this post you need to login first.

13 Comments

You must be Logged on to comment or reply to a post.

  1. Vincent Holland-Keen

    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 🙂 .

    (0) 
    1. Vincent Holland-Keen

      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:

      • Sets the document.domain of the HTML Viewer iFrame
      • Specifes a function that accepts a text string from a field within the HTML Viewer and uses it to set the value of a text box in the parent Personas iFrame:

      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;

      (0) 
        1. Vincent Holland-Keen

          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).

          (0) 
          1. Clemens Gantert

            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

            (0) 
  2. Gaurav Anadkat

    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

    (0) 
      1. Gaurav Anadkat

        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

        (0) 
  3. Srihari Tummala

    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

    (0) 
    1. Ian Rayner

      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

      (0) 
      1. Srihari Tummala

        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

        (0) 

Leave a Reply