Skip to Content

I saw many beautiful blogs around this topic, some among the others are:

Anyway, my goal is to put all together and have a Custom Tile type that is also able to show a Microchart (like the ones on SAP Smart Business). Moreover, it should also be connected to an oData service to give some “life” to the tile itself.

In this example we will create a Tile dedicated to Managers: it will have an embedded ComparisonMicroChart that shows the average of Employees Overtimes over the last 3 months.

So let’s start!

1. Create the Tile

Technically speaking, as stated in the standard help guide here, a Custom Tile can be set up by creating an SAPUI5 View, register it as a CSR CHIP on the server and add it to the Tile Catalog configuration.

What it’s not really stated is that if you need a Dynamic Tile (or even a Static one, but with navigation capabilities) this is not completely straightforward.
In order to have it “live”, many rules have to be followed as the new Tile must be developed in such a way to be compatible with all the frameworks that are around the Launchpad itself, like Configuration, Navigation, Semantic Objects assignments, and so on.

Luckily, almost the entire job has been already done by our folks at SAP, so we can start from a working template rather than from scratch.

1.1 Enter sap-ui-debug

Open up /UI2/FLPD_CUST and attach the parameter sap-ui-debug=true to the URL, in order to have something like this:

http://:/sap/bc/ui5_ui5/sap/arsrvc_upb_admn/main.html?scope=CUST&sap-client=100&sap-language=IT&sap-ui-debug=true

Load it and…be patient, libraries in debug mode take a while to get processed.

As soon as you have access to your launchpad, open Chrome Developer tools and navigate to “Network” tab (if you don’t see anything, you might need to reload the page).
Enter “Dynamic” in the filter field and check the loaded files:

We need the contents of DynamicTile-dbg.controller.js but we can skip DynamicTile-dbg.view.js as we will create our own UI later on.
At this point you shall also download the contents of applauncher_dynamic.chip.xml.

Now, leaving Developer Tools open, go back to the Launchpad Customization page and click on any of the Dynamic Tiles to get to its configuration page.
Then, go back to the Network tab in the Developer Tools and this time search for “Configuration”:

We need the contents of both Configuration.view.xml and Configuration-dbg.controller.js.

Pay attention to the url of these files: they must belong to the sap/ushell/components/tiles/applauncherdynamic repository.

With these sources, we are now ready to create our tile.

1.2 Create the Project

For the purpose of this tutorial, I created a simple project using Eclipse – the reason why I did not use the SapWebIDE is because I do not have a correctly set environment for the deployment from the Cloud (I am lazy and the Eclipse Team Provider is faster than a manual Download/Upload 🙂 ).

The structure of the project is straightforward:

Configuration.controller.js and Configuration.view.xml are copies of the original sources download in the previous chapter, slightly changed only to rewrite original component namespaces.

KpiTile.controller.js is a changed copy of the original DynamicTile.controller.js source.

KpiTile.view.xml is the new Tile layout file.

kpilauncher_dynamic.chip.xml is a changed copy of the original applauncher_dynamic.chip.xml source.

1.2.1 View Layout

Open up KpiTile.view.xml and create your own tile. You can use any tile type (StandardTile, CustomTile, GenericTile), but remember that the only supported frame type is OneByOne – I wasn’t able to have it working using the TwoByOne frame, but maybe someone else will be 🙂

Also, GenericTile is the one that works best with contents like Microcharts.

Here is my example:

<?xml version="1.0" encoding="UTF-8"?>
<core:View 
		xmlns="sap.suite.ui.commons" 
		xmlns:ui="sap.suite.ui.microchart" 
		xmlns:core="sap.ui.core" 
		controllerName="customtilechips.KpiTile">
		
     <GenericTile 
  	 	id="kpiTile"
	 	press="onPress" 
	 	header="{/config/display_title_text}" 
	 	subheader="{/config/display_subtitle_text}" 
	 	frameType="OneByOne">
        <tileContent>
        	<TileContent footer="{/config/display_info_text}">
            	<content>
                	<ui:ComparisonMicroChart scale="M">
						<ui:data>
							<ui:ComparisonMicroChartData title="{/data/emp/month1}" value="{/data/emp/value1}" color="{/data/emp/color1}" />
							<ui:ComparisonMicroChartData title="{/data/emp/month2}" value="{/data/emp/value2}" color="{/data/emp/color2}" />
							<ui:ComparisonMicroChartData title="{/data/emp/month3}" value="{/data/emp/value3}" color="{/data/emp/color3}" />
						</ui:data>
					</ui:ComparisonMicroChart>
				</content>
			</TileContent>
		</tileContent>
	</GenericTile>
	
</core:View>

 

Let’s have a look at the bindings.
The Tile shall display a static title and a static subtitle customized in the configuration properties of the Tile itself.
On the other hand, the embedded Microchart shall display live data, coming from an oData service.

The “config” property of the configuration model of every Dynamic Tile has these properties:

  • display_title_text
  • display_subtitle_text
  • display_icon_url
  • display_info_text
  • display_number_unit

The “data” property of the configuration model has the dynamic properties incoming from any configured oData service Url that can drive data on a DynamicTile:

  • icon
  • info
  • infoState
  • number
  • numberDigits
  • numberFactor
  • numberState
  • numberUnit
  • stateArrow
  • subtitle
  • targetParams
  • title

In addition to these properties, remember that the oData service can return as many properties as you want, so – with a little trick – the Tile will not be limited to display only these.

Moreover, do not change the “onPress” event handler registered to the Tile press event: this is already managed in the original Controller file and it works fine.
If you want to have your Tile doing something else rather than navigate you to some content, than change the event handler and implement your specific behavior in the Controller object.

1.2.2 Changing the Controller

Now open the KpiTile.controller.js file as we need to do some changes to the code copied from the standard.

Important: the standard code declares the sap.ushell.components.tiles.applauncherdynamic.DynamicTile.controller. Remember to change this in the controller declaration and all other references! The namespace and repository structure must be consistent!

Update:
After checking in different systems, I noticed that in some cases the code within the controller requires the load of a library named sap.ushell.components.tiles.utilsRT, whereas on others only the sap.ushell.components.tiles.utils library is loaded.
I didn’t investigate (maybe some Mentor has an answer here), but in the system where this project has been deployed the utilsRT library is not existing so I went through the controller code and changed all the references to utilsRT simply renaming them to utils (and therefore pointing all the references to sap.ushell.components.tiles.utils) – a “Find and Replace” in Eclipse or WebIDE is enough.

Scroll down the code util you reach the successHandleFn function: this is the Success callback for each oData call that the Tile makes to the registered Service Url and here we will do a quick enhancement to take into account additional incoming data.

        successHandleFn: function (oResult) {
            var oConfig = this.getView().getModel().getProperty("/config");
            this.oDataRequest = undefined;
            var oData = oResult,
                oDataToDisplay;
	            if (typeof oResult === "object") {
	                var uriParamInlinecount = jQuery.sap.getUriParameters(oConfig.service_url).get("$inlinecount");
	                if (uriParamInlinecount && uriParamInlinecount === "allpages") {
	                    oData = {number: oResult.__count};
	                } else {
	                    oData = this.extractData(oData);
	                }
	            } else if (typeof oResult === "string") {
	                oData = {number: oResult};
	            }
            oDataToDisplay = sap.ushell.components.tiles.utils.getDataToDisplay(oConfig, oData);
            // Begin Change --------------------------->
			// Use "emp" property to store original data
            var aKeys =
            	[
            	 	// Additional data for our KPI Tile //
            	 "month1", "month2", "month3", "value1", "value2",
            	 "value3", "color1", "color2", "color3"
            	 		// End additional data //
            	 ];
            var sName; 
            // Prepare emp object:
            oResult.results = {};
            for ( var i = 0; i < aKeys.length; i++){
            	sName = aKeys[i];
            	oResult.results[sName] = oResult[sName];
            }
            // Store the additional results back to emp
            oDataToDisplay.emp = oResult.results;
            // End Change <---------------------------
            
            // set data to display
            this.getView().getModel().setProperty("/data", oDataToDisplay);

            // rewrite target URL
            this.getView().getModel().setProperty("/nav/navigation_target_url",
                sap.ushell.components.tiles.utils.addParamsToUrl(
                    this.navigationTargetUrl,
                    oDataToDisplay
                ));
        },

The core of the change is in the middle, right after the “Begin Change” mark: the oData linked to our tile returns a set of additional properties used to load the Microchart.

We store these properties in an array named aKeys and for each one of them, we loop the oData structure oResult moving any additional property to its result property. As a last step, we fill the emp property of oDataToDisplay: the emp property is the one binded into the View object.

In this example, additional properties are statically defined in the code: with some more advanced JavaScript, everything can be completely transformed to dynamic.

1.2.3 Change the CHIP definition file

The standard documentation for custom Tiles creation refers to the term CSR CHIP, which stands for Client-Side Rendered CHIP.

Basically, a CHIP within the system is described by a metadata XML file which contains some very important properties: these properties are interpreted at runtime by the Launchpad engine to understand if a registered CHIP can be used as a Tile or not.

At this page is explained the schema of an XML CHIP definition file.

For our example, the change is pretty straightforward and everything it’s done by simply changing the path to our SAPUI5 view in the <sapui5> node of the original applauncher_dynamic.chip.xml file.

So make a copy of it and change it as follows:

<?xml version="1.0" encoding="UTF-8"?>
<chip xmlns="http://schemas.sap.com/sapui2/services/Chip/1">
    <implementation>
        <sapui5>
            <viewName>customtilechips.KpiTile.view.xml</viewName>
        </sapui5>
    </implementation>
    <appearance>
        <title>Dynamic Applauncher with Microchart</title>
        <description>Dynamic KPI Applauncher Chip</description>
    </appearance>
    <contracts>
        <consume id="configuration">
            <parameters>
                <parameter name="tileConfiguration"></parameter>
            </parameters>
        </consume>
        <consume id="preview" />
        <consume id="writeConfiguration" />
        <consume id="configurationUi" />
        <consume id="search" />
        <consume id="refresh" />
        <consume id="visible" />
        <consume id="bag" />
        <consume id="url" />
        <consume id="actions" />
        <consume id="types">
            <parameters>
                <parameter name="supportedTypes">tile</parameter>
            </parameters>
        </consume>
    </contracts>
</chip>

You can also change the title and description of the Tile (it will be shown in the “Add new tile” screen of the Launchpad configuration) and customize the supportedTypes property.
In my case, I only need a Tile so I’ve limited the property value to “tile”. Another suitable value is “link”, which must then be supported at runtime by additional coding (see the original DynamicTile-dbg.controller.js file for reference).

As previously said, the most important property here is the <viewName> under <sapui5>.
Remember that this property must contain the full name of the view – including the namespace – relatively to CHIP definition XML file.

So if your XML file is under:

https://:/sap/bc/ui5_ui5/sap/Y_FLP_KPITILE1/kpidynamic_launcher.chip.xml

Than the view name is relative to this file, so:

customtilechips.KpiTile.view.xml

2. Deploy the tile

Let’s do a quick review of what we have done so far.

  1. Download necessary template files directly from your system
    1. DynamicTile-dbg.controller.js
    2. Configuration-dbg.controller.js
    3. Configuration.view.xml
    4. applauncher_dynamic.chip.xml
  2. Create a new SAPUI5 project and import the downloaded sources
  3. Cleanup the sources (change namespaces, file names, controller names, and so on)
  4. Create a new XML view containing the UI for the custom tile you want to create (in this example, KpiTile)
  5. Change the Tile controller object in order to process additional oData properties
  6. Check and change the CHIP definition file

Done? Good, so let’s deploy the project to the ABAP SAPUI5 Repository.

After the deployment, you should see a newly created BSP Application with a structure that mirrors your project:

2.1 Register the CHIP

This is by far the most simple step but at the same time the most important one.

Without registering the CHIP, the Tile will never be visible in the Catalog so it will not be possible to use it in any Launchpad.

Open transaction /UI2/CHIP and create a new Chip definition.
Name it as you want, I named it exactly as the BSP Page for clarity:

Use the absolute – without hostname – path to your chip definition file as the URL.
While saving, the system validates all the entries so you’ll be sure that the file can actually be found.

Save this newly created CHIP and go to SE80.

Search for WebDynpro Application CUSTOMIZE_COMPONENT, then execute it.
At this point, we need to enhance the /UI2/CHIP_CATALOG_CONFIG component by adding our CHIP definition to the component configuration /UI2/FLPD_CATALOG.

This configuration is taken into account by the Launchpad application configuration component: Tiles defined as CHIPS in this configuration are instantiated and can then be used in Launchpads.

In the browser window, set parameters as follows then hit “Continue in Change Mode”. If the configuration ID is not existing, go on and create it.

In the next window, we are going to add our CHIP as a configurable tile in Launchpads.

From the Configuration Context table on the left, open node “parameterMultiVal”, select its line and click “New -> values” from the Toolbar.

On the right, in the input field, write:

X-SAP-UI2-CHIP: <your_chip_name>

Where the chip name is the same used in transaction /UI2/CHIP

Save and close the browser.

2.2 Try to load it

Now that our Tile is deployed and registered as CHIP, let’s try to add it to one of our Launchpads.

First of all, I noticed (as explained in this blog) that cleaning up the ICM cache helps in this cases, so if you have proper authorization open SMICM transaction and head to “Go-To -> HTTP Plug-In -> Server Cache -> Invalidate locally” (or globally, depending on your installation).

Open transaction /UI2/FLPD_CUST and select one existing catalog. You might need to clean the browser cache, so it loads fresh files from the server.

As soon as it loaded, hit the “+” sign on the page to add a new Tile and – if everything worked as expected – you shall be able to see your shiny new Custom Tile:

Select the tile and you shall be presented with the Configuration page, in which you can set base parameters (remember the ones stored under the /config/ path of the Tile model).

At the moment, even if we add the tile as it is, nothing is shown on our Microchart because we miss a proper oData service.
Remember that in our case, the service must return a specific set of properties:

  • month1
  • value1
  • color1
  • month2
  • value2
  • color2
  • month3
  • color3
  • value3

Keeping this is mind, go on and create a very simple oData service that returns a value for each one of these properties.

Go to SEGW transaction on your Gateway system, create a new service with at least one EntityType and EntitySet. The EntityType must expose properties necessary to be consumed by the Tile (refer to paragraph 1.2.1) plus our custom properties.

With the oData service URL ready, configure it in the Tile options and go back to the catalog.

At this point, your Launchpad Customization page should show you something similar to this:

3 Use the Tile

Using a Username linked to the catalog you have changed, log on to the Fiori Launchpad or execute transaction /UI2/FLP.

If you don’t see the new Tile, it could be because it is not added to the personalized catalog yet: click on the Pencil icon on the lower right of the screen and you’ll be presented with the customization page.
From here, look for the custom tile and add it by clicking the “+” sign.

Our custom tile should now be there, ready to receive the first update from the underlying oData service you created.

Here’s the expected result:

If you have defined a Semantic Object navigation, you should also be able to click on the Tile and get navigated to the application you have configured.

4 Closure

Following this rather simplicistic approach (in which – to be honest – the most of the work has been already done by SAP guys creating the DynamicTile definitions) a complete new set of custom tiles can be created on the OnPremise Launchpad as well.

Charts, Images, almost any control that can fit the “content” aggregation of the GenericTile component can be used – just pay attention on how you want to feed it and change the Tile controller accordingly to your needs.

I hope that this can bring some help to anyone that needs to deal to a similar situation, and also that new ideas can be presented to the community.
A really good trick would be to have a the option to select the kind of Microchart on the Tile, and have it completely dynamic and configurable.

If you need a copy of the original coding, drop me a comment and I will place it on a GitHub repository for reference.

Update: here is the repository URL on GitHub

Enjoy your new Tile and all the best!
Roberto.

To report this post you need to login first.

17 Comments

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

  1. Denis D.

    Hi Roberto,

    thanks for sharing the topic!

    I prepared all steps you described (finally I used your coude) but I was not able to get the tile running.

    I see the Tail in my launchpad with a message “Cannot load tile”.

    In the console I have an Error:

    Call to success handler failed: sap.ushell.components.tiles.utils.getConfiguration is not a function – TypeError: sap.ushell.components.tiles.utils.getConfiguration is not a function
    at constructor.onInit (…./customtilechips/DumpTile.controller.js:18:49).

    Do you have an Idea, what went wrong?

     

    Thank you

    Denis

     

    (0) 
    1. Roberto Pagni Post author

      Hi Denis,
      it seems to be related to the standard library sap.ushell.components.tiles.utils.

      Can you double check in your DumpTile.controller.js, at the very beginning, if you can find this line:

      jQuery.sap.require("sap.ushell.components.tiles.utils");

      And if the corresponding module can actually be loaded? Search for it using Chrome Developer tools -> Network tab

      Also, check around line 118:

      var oConfigurationUi = sap.ushell.components.tiles.utils.getConfigurationUi(oView, "customtilechips.Configuration");

      The last parameter (here “customtilechips.Configuration”) must match your “namespace.viewname”.

      Cheers,

      R.

       

      (1) 
      1. Denis D.

        Hi Roberto,

        thanks for the response and hints.

        The error was caused by the action described in ‘1.2.2 Changing the Controller’ utilsRT replacement.

        In my IDE 

        jQuery.sap.require("sap.ushell.components.tiles.utilsRT");

         is unknown but the library seems to be mandatory in the system.  

         

        I reversed the ‘utilsRT’ replacement and added the both libraries

        jQuery.sap.require("sap.ushell.components.tiles.utils");
        jQuery.sap.require("sap.ushell.components.tiles.utilsRT");

        and this solved my issue.

         

        Thanks

        Regards

        Denis

        (1) 
        1. Roberto Pagni Post author

          Hello Denis,

          good to know,glad it worked! This actually means that depending on the release the utilsRT library is indeed needed: as you noticed, my system did not use them,that’s why I suggested the replacement.

          I will try to investigate more 🙂

          Cheers,

          Roberto.

          (0) 
  2. Pasin Siwaamornratana

    Hi Roberto,

    Thank you for sharing. I have already try it by myself and I want to share this for problem TwoByOne frame type.

    By add parameter name “col” as 2 as below you can able to use tile with frame type TwoByOne, but in my system tile would support only 2×1 or 1×1 size. (I’m not sure in other system is possible to use other size)

     

    Don’t forget to use your tile frameType to TwoByOne in tile view.

    .

    (1) 
  3. ramees rahath

    Hi Roberto Pagni,

    Thanks for the great blog.I have followed the blog and created and registered the chip.But i am unable to see the tile template.I can see the default(dynamic,static and news) templates onlyPlease advice me on the steps to resolve the issue.

    Thanks and Regards,

    Ramees

    (0) 
  4. ramees rahath

    Hi Roberto Pagni,

    I have resolved the issue regarding the tile template.But i am not able to open the configuration view when i click on my custom tile.Please find the error log

    Hope you can provide me a solution

    Regards

    Rameez

     

    (0) 
    1. Roberto Pagni Post author

      Hello Rameez,

      it seems an issue with the CHIP definition XML file. It clearly says that the “types” contract is not supported, which should definitely be.

      Please double check the kpilauncher_dynamic.chip.xml file and the original Configuration-dbg.controller.js file.
      You can also try to debug the Configuration-dbg.controller.js to understand why and where it fails.

      Regards,

      R.

      (0) 
  5. Philipp Steidl

    Hi,

     

    thanks for that turorial!

     

    we are using fiori 2.0 and your version didn´t work for me.

    The problem was, that the view.xml didn´t provided the needed function “getTileControl”.

    this is how i got it running:

    my Project:

    but different than the tutorial above i copied the source from: /sap/bc/ui5_ui5/ui2/ushell/resources/sap/ushell/components/tiles/applauncherdynamic/DynamicTile-dbg.view.js

    and then changed it to: customtile.CustomRadialChartTile.view.js

    // Copyright (c) 2009-2014 SAP SE, All Rights Reserved
    
    (function() {
    	"use strict";
    	/*global jQuery, sap */
    	/*jslint nomen: true */
    
    	jQuery.sap.require("sap.suite.ui.microchart.RadialMicroChart");
    
    	sap.ui.jsview("customtile.CustomRadialChartTile", {
    		getControllerName: function() {
    			return "customtile.CustomRadialChartTile";
    		},
    		createContent: function(oController) {
    			this.setHeight('100%');
    			this.setWidth('100%');
    		},
    		tofloat:function(value){
    			return parseFloat(value);
    		},
    		getTileControl: function() {
    			jQuery.sap.require('sap.m.GenericTile');
    			var oController = this.getController();
    			
    			var microchart = new sap.suite.ui.microchart.RadialMicroChart({
    					/*	percentage: '/data/display_number_value}',*/
    						total: 100,
    						valueColor: '#0273C1'
    					});
    			
    			microchart.bindProperty("percentage", {
    				path: '/data/display_number_value',
    					formatter: function(fValue) {
    						return parseFloat(fValue);
    					},
    					type: new sap.ui.model.type.Float()
    				});
    
    			return new sap.m.GenericTile({
    				header: '{/data/display_title_text}',
    				subheader: '{/data/display_subtitle_text}',
    				size: "Auto",
    				frameType: "OneByOne",
    				tileContent: [new sap.m.TileContent({
    					size: "Auto",
    					footer: '{/data/display_info_text}',
    					unit: '{/data/display_number_unit}',
    					//We'll utilize NumericContent for the "Dynamic" content.
    					/* content: [new sap.m.NumericContent({
    					     scale: '{/data/display_number_factor}',
    					     value: '{/data/display_number_value}',
    					     truncateValueTo: 5,//Otherwise, The default value is 4.
    					     indicator: '{/data/display_state_arrow}',
    					     valueColor: '{/data/display_number_state}',
    					     icon: '{/data/display_icon_url}',
    					     width: '100%'
    					 })]*/
    					content: [microchart]
    				})],
    				press: [oController.onPress, oController]
    			});
    		},
    		getLinkControl: function() {
    
    			if (window.location.search.indexOf("new_links_container=true") === -1) {
    				jQuery.sap.require('sap.m.Link');
    				return new sap.m.Link({
    					text: "{/config/display_title_text}",
    					href: "{/nav/navigation_target_url}",
    					//set target formatter so external links would be opened in a new tab
    					target: {
    						path: "/nav/navigation_target_url",
    						formatter: function(sUrl) {
    							if (sUrl && sUrl[0] !== '#') {
    								return "_blank";
    							}
    						}
    					}
    				});
    			} else {
    				jQuery.sap.require('sap.m.GenericTile');
    				var oController = this.getController();
    				return new sap.m.GenericTile({
    					mode: sap.m.GenericTileMode.LineMode,
    					header: '{/config/display_title_text}',
    					subheader: '{/config/display_subtitle_text}',
    					press: [oController.onPress, oController]
    				});
    
    			}
    		}
    	});
    }());

    and afterwards i changed the viewName at CustomRadialChart.chip.xml the following:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Copyright (c) 2009-2014 SAP SE, All Rights Reserved -->
    <chip xmlns="http://schemas.sap.com/sapui2/services/Chip/1">
    	<implementation>
    		<sapui5>
    			<!--<basePath>../resources</basePath>-->
    			<viewName>customtile.CustomRadialChartTile.view.js</viewName>
    		</sapui5>
    	</implementation>
    	<appearance>
    		<title>RadialMicroChart</title>
    		<description>Prozentanzeige OneByOne</description>
    	</appearance>
    	<contracts>
    		<consume id="configuration">
                <parameters>
                    <parameter name="tileConfiguration"></parameter>
                   <parameter name="col">1</parameter>
                </parameters>
            </consume>
            <consume id="preview" />
            <consume id="writeConfiguration" />
            <consume id="configurationUi" />
            <consume id="search" />
            <consume id="refresh" />
            <consume id="visible" />
            <consume id="bag" />
            <consume id="url" />
            <consume id="actions" />
            <consume id="types">
                <parameters>
                    <parameter name="supportedTypes">tile</parameter>
                </parameters>
            </consume>
    	</contracts>
    </chip>

     

    regards

    Philipp

     

    (0) 
  6. Kristen Woodley

    Hello Roberto,

    This was an excellent blog post! Thank you so much for sharing. While working through the steps you provided I am experiencing the same issue as ramees rahath. After I load everything into my gateway system per your instruction I am unable to open configuration for the custom tile and I receive errors in the console:

    “sap.ui2.srvc.Chip({sChipUrl:”/sap/bc/ui5_ui5/sap/Y_FLP_KPITILE1/kpilauncher_dynamic.chip.xml”}): Contract ‘types’ is not supported –  sap.ui2.srvc.Chip”

    Upon further investigation I found that when calling oView.getViewData() in the KpiTile controller it is returning undefined, so oViewData.chip isn’t being retrieved. I spent some time debugging the original from the launchpad customization page and it has no trouble retrieving oViewData.chip.

    Any suggestions as to why getViewData is coming back undefined?

    (0) 

Leave a Reply