Array Properties in SAP Design Studio Custom Components

ALMA CorrelatorWhilst coding a custom component for a complex SAP Design Studio Dashboard I was vaguely aware that coding one property per required column property was not exactly what you’d call, scalable! What I needed was to be able to capture arrays of data instead of just single properties, that way I could just have iterative code in the component which would take care of my component generation instead of these hardcoded single properties. In this instance I wanted  to capture the colours for a given column header ( for a data table). In my situation I had 10 headers but as this is a customisable component I guess it would be better to express this as ‘n’ rather than 10. Despite posts to the contrary this is indeed made possible by use of the Array data type.

First we start with writing the property into the contribute.xml file. This is really the key to our issue and once this is correct the rest is really plain sailing. We start by writing the following property XML (I find the xml to be much easier than using the GUI) into our file:-

        <property id="columnColours" type="Array" title="Column Colours Array">
            <property id="arrayEntry" type="String" title="Column Colour">
            </property>
        </property>

The outer xml snippet entitled ‘columnColours’ creates an array to house the array data whilst the inner section entitled ‘arrayEntry’ details what I consider to be an array template/type definition for the type of data you wish to store; in this instance the type of data that we wish to store is a string value representing a colour value.  Once this has been defined and the changes saved your array is nearly there. All that remains is to back this up with the accessor function to allow the Design Studio to inject your settings into the main body of the custom component .js file. This function is defined like any other accessor function, identical!

    
    this.columnColours= function(value) {
        if (value === undefined) {
            return columnColours_data;
        } else {
            columnColours_data = value;
            return this;
        }
    };

From here you can now access your property_data which instead of containing a primitive data type now contains an array of the type you defined. You can thus use iterative methods to access the values. Of course, you are all hardened professionals in the javascript world… You won’t do what I do and use pop/shift on the passed array and then wonder why subsequent redraws failed to work;-) That little problem I solved by cloning my source array using the very simple syntax to return an new shallow copy.

var newArray = sourceArray.slice(0);

//Magic happens here, EVERY TIME, not just first time!

Once your code has all been defined you can now  run up the custom component in SAP Design Studio and if you look at the properties grid for your custom component you should see the new property there showing 0 entries. If you then click on the ellipsis to the right of the edit field you should be provided with a dialog similar to that below which allows you to specify the members of your array.  Once you have defined your members your code should be good to go.

2016-04-13_2217

Far more scalable than 9 separate properties, I’m sure you’ll agree.

Go do it.

Theory of Relativity

Relativity-formula

Recently whilst putting a .NET web project to bed we had yet more reports from the field of some rendering issues with Internet Explorer 7.  Of course we had tested IE 7 using the compatibility modes present within later versions of IE but seemingly this does not throw up many issues that arise in the ‘real’ browser

I know, I know…., in an ideal world we’d all have the latest and greatest browsers but in the real world companies are not so ‘lithe’ when it comes IT infrastructure.

The issue being reported was that one form displaying an accordion control, which in turn had embedded within it a scrollable div which in turn contained three or four divs as well as a JQGrid was having ‘issues of parentage’… When the main scrollable div was being scrolled all underlying divs were being rendered correctly and scrolling with the content whilst the JQGrid was not moving at all, just merely floating in space and generally getting in the way. After a lot of digging I found the following solution that cured my issues:- The main HTML was defined thus

<div id=”QuestionnaireGridContainer”>
<table id=’QuestionnaireGrid’></table>
</div>

 with the ‘QuestionnaireGrid’ being the location that the javascript JQGrid is rendered into, The issue however seems to lie with the parent div and not the JQGrid itself, what I needed to do was attach a style via the css to the ‘QuestionnaireGridContainer’ div like thus:

#QuestionnaireGridContainer{
    position:relative;
}

This ‘position:relative’ style  in this instance seems to explicitly force all children objects to behave correctly with regard to their location within time and space and now when i scroll my div content all child objects are scrolled exactly as I would expect.

Float Like a Butterfly….Errr

floatlabRecently whilst putting a .NET web project to bed we had some reports from the field of some rendering issues with Internet Explorer 7.  Of course we had tested IE 7 using the compatibility modes present within later versions of IE but seemingly this does not throw up many issues that arise in the ‘real’ browser

I know, I know…., in an ideal world we’d all have the latest and greatest browsers but in the real world companies are not so ‘lithe’ when it comes IT infrastructure.

One such issue being reported was along the following lines:-

Our application was divided into a standard navigation-detail model with the navigation part of the equation being a host of controls displayed within an accordion located to the left hand side of the screen and the detail being displayed to the right hand side of the screen in line horizontally with the accordion. This was achieved through the div hosting the ‘Detail’ section having a ‘float:right’ instruction within the css like so:-

.mainPageContainer{
    display: inline-block;
    float: right;
    height: 75%;
    width: 74%;
}
 
This seemed to work without issue throughout the entire development cycle… We targeted the following browsers:-
 
Internet Explorer 8/9/10 (and 7 in compatibility mode!)
Chrome
Firefox
Opera
Safari
 
All seemed to display without issue, it was left to ‘the real’ Internet Explorer 7 to lower the tone…. Instead of rendering the detail directly to the right of the accordion IE 7 rendered the details BELOW and to the right of the accordion. The fix turned out to be very simple though NOT obvious to me although with hindsight I do kind of understand, the issue was css related  but not with my mainPageContainer, rather the issue was with my accordionContainer which was defined thus:-
 
.accordionContainer{
    height:650px;
    min-width:20%;
    width:25%;
    display:inline-block;
}
 
It is not obvious, to me at least, that in order for any items placed to the right of the accordion to float right ‘properly’ the accordion container itself should be explicitly be made to float left (which it seems to do very well itself without instruction!). Thus I made the following bold changes:-
 
.accordionContainer{
    height:650px;
    min-width:20%;
    width:25%;
    display:inline-block;
    float:left;
}

 Voila, now even Internet Explorer 7 can now correctly render our application,

 

 
 

Displaying Alternative Coloured Images in JQ Grid

RedAmberGreen

Whilst coding up a recent .NET MVC application built heavily using JQGrid I came across a requirement which to be honest I struggled with at first . In short I wanted to, on a column by column basis, colorise the icons that were to be displayed. Now JQGrid supports a icon colorset which seemingly cannot be changed easily so in the end I had to resort to changing the styles. Here is how I achieved this, warts and all….

Firstly of course JQGrid uses the standard set of JQuery Ui Icons to provide its user interface. I say ‘Icons’ but this is of course provided as one image which is broken up into sprites of 16 x 16 pixels, more of that later. So the first step is to download the set of colour icons that you require…. JQueryUI.com provides a standard set of these icons rolled in every colour you can dream so its in fact very easy. The hardest part is arriving at your chosen colours!

I arrived at the following soft representation of the Red, Amber and Green colours :-

  • Red Hex: FF99A3
  • Amber Hex: FFB380
  • Green Hex: 99FFB8

So using these hex values i was able to download my chosen colour schemes using the following url:-

In my instance i downloaded the following files:-

As you can see from the screen grab below the resultant images are in fact compound images containing many 16 x 16 sprites:

FF99A3

In my case I need to use the following icons Bullets and Ticks, as you can see if you examine the image the tick sprite is the fifth sprite across on the tenth line down whilst the bullet is the sixth character on the same line. Given that each character is 16 pixels wide and tall finding the location is relatively easy. The basic formula is (n-1) x16  expressed as a negative where n is the line number or character across.  So in the case of our tick as it is the fifth character across its horizontal location starts at -64 pixels, similarly its vertical location is -144 pixels (9 x 16 expressed as a negative)

We thus need to create some css classes to represent these icons so in my instance I created the following classes:-

.ui-green-tick{
width:16px;
height:16px;
background-image:url(../css/redmond/images/ui-icons_99FFB8_256x240.png)!important;
background-position:-64px -144px
}
.ui-red-tick{
width:16px;
height:16px;
background-image:url(../css/redmond/images/ui-icons_FF99A3_256x240.png)!important;
background-position:-64px -144px
}
.ui-amber-tick{
width:16px;
height:16px;
background-image:url(../css/redmond/images/ui-icons_FFB380_256x240.png)!important;
background-position:-64px -144px
}

.ui-green-bullet{
width:16px;
height:16px;
background-image:url(../css/redmond/images/ui-icons_99FFB8_256x240.png)!important;
background-position:-80px -144px
}
.ui-red-bullet{
width:16px;
height:16px;
background-image:url(../css/redmond/images/ui-icons_FF99A3_256x240.png)!important;
background-position:-80px -144px
}
.ui-amber-bullet{
width:16px;
height:16px;
background-image:url(../css/redmond/images/ui-icons_FFB380_256x240.png)!important;
background-position:-80px -144px
}

Note that each class points to the relevant colour image using the background-image:url property, in order to ensure that our image takes precendence over other images referred to in the JQGrid style sheets I habd to also use the !Important declaration, The sprite location is specified using the background-position: properties using the algorithm discussed above. As you can see we also specify the size of our images. in this instance 16 x 16 pixels.

OK, so believe it or not thats the worst part over with, all that is left now is for you to determine on a row by row and column by column basis which icons are to be displayed and change the relevant classes using javascript I defined the following javascript helper functions, some code has been removed for brevity so they may not be compilable (apologies in advance).

function OnLoadPrimeGridExclusive(grid, actionColumn, theTitle, dataColumn) {

    var iCol = getColumnIndexByName(grid, actionColumn);
    grid.children(“tbody”)
            .children(“tr.jqgrow”)
            .children(“td:nth-child(” + (iCol + 1) + “)”)
            .each(function () {
                $(“<div>”,
                    {
                        title: theTitle,
                        mouseover: function () {
                            $(this).addClass(‘ui-state-hover’);
                        },
                        mouseout: function () {
                            $(this).removeClass(‘ui-state-hover’);
                        },
                        click: function (e) {

                            /* Get the Contexts that we are using here. We need to know the Row
                                and the span in order to accomplish this task……*/

                            var row = $(e.target).closest(“tr.jqgrow”);
                            var span = $(e.target).closest(“span.ui-icon”)

                            /* And now call the Toggle Routine that will toggle the selection */

                            ToggleSelectedSelection(row, span, grid, dataColumn, true);

                            grid.saveRow($(row).attr(“id”), {
                                aftersavefunc: function (id, response, options) {
                                    var someRetValue = response; // set someRetValue to any value
                                }
                            });
                        }
                    }
                  ).css({ “margin-left”: “5px”, float: “left” })
                   .addClass(“ui-pg-div ui-inline-custom”)
                   .append(‘<span id=””></span>’)
                   .appendTo($(this).children(“div”));

                var row = $(this).closest(“tr.jqgrow”)
                var span = $(this).find(“span.ui-icon”);
                ToggleSelectedSelection(row, span, grid, dataColumn, false);

            });
    }
}
function ToggleSelectedSelection(row, span, grid, fieldName, toggle) {

    /* Get the Row id for the current row */

    var rowID = $(row).attr(“id”);
    var unselected =  bulletClass(fieldName);
    var selectedClass = tickClass(fieldName);

     /* And get the current selected value */

    var selected = grid.jqGrid(‘getCell’, rowID, fieldName);
    var removeClass = null;
    var addClass = null;
    var selectionState = null;

    /* Now work out what we need to show */

    if (toggle) {
        removeClass = (selected == ‘false’ || selected == false) ? unselected : selectedClass;
        addClass = (selected == ‘false’ || selected == false) ? selectedClass : unselected;
        selectionState = (selected == ‘false’ || selected == false) ? “true” : “false”;
    } else {
        addClass = (selected == ‘false’ || selected == false) ? unselected : selectedClass;
        removeClass = (selected == ‘false’ || selected == false) ? selectedClass : unselected;
    }

    /* And do that thing! */

    $(span).removeClass(removeClass);
    if (addClass != “”) {
        $(span).addClass(addClass);
    }
    if (selectionState != null) {
        grid.editRow(rowID);
        grid.jqGrid(‘setCell’, rowID, fieldName, selectionState, “”);
        grid.saveRow(rowID, {
           aftersavefunc: function (id, response, options) {
              }

         }
        );
    }
}

function bulletClass(fieldName) {
    switch (fieldName) {
        case “Red”: {
            return “ui-red-bullet”;
        }
        case “Amber”: {
            return “ui-amber-bullet”;
        }
        case “Green”: {
            return “ui-green-bullet”;
        }
        default: {
            return “ui-icon-bullet”;
        }
    }
}

function tickClass(fieldName) {
    switch (fieldName) {
        case “Red”: {
            return “ui-red-tick”;
        }
        case “Amber”: {
            return “ui-amber-tick”;
        }
        case “Green”: {
            return “ui-green-tick”;
        }
        default: {
            return “ui-icon-check”;
        }
    }
}
function  getColumnIndexByName (grid, columnName) {
    var cm = grid.jqGrid(‘getGridParam’, ‘colModel’), i = 0, l = cm.length;
    for (; i < l; i += 1) {
        if (cm[i].name === columnName) {
            return i; // return the index
        }
    }
    return -1;
}

And in the loadComplete event of the grid the following javascript starts the whole process….. If you are only colourising one column you need only call this routine once

OnLoadPrimeGridExclusive(grid, ‘RedAction’, ‘De/Select Red Rating’, ‘Red’, true);
OnLoadPrimeGridExclusive(grid, ‘AmberAction’, ‘De/Select Amber Rating’, ‘Amber’,true);
OnLoadPrimeGridExclusive(grid, ‘GreenAction’, ‘De/Select Green Rating’, ‘Green’, true);

A few notes that need to be made here, for each display column where the actual image is displayed ticked or not in this instance the columns RedAction/GreenAction/AmberAction there is also a hidden checkbox type data column which physically stores the boolean value indicating whether or not the grid is ticked or not (entitled Red/Green/Amber). This column is manipulated whenever the user clicks on an image to toggle the selection and the icon is set according to the underlying value of that column.

My time with JQGrid on this particular project has not been a happy one, I started off using the .NET MVC implementation but have overtime reverted to using the plain old javascript version as it offers a great deal more control and configurability. I think we may have turned a corner now and so thought I’d like to share some of that positivity with the community.

I hope this helps.

Setting Default Data for Add New operation using .NET MVC JQGrid

Capture

Of late I have been working with the .NET MVC JQGrid control for a relatively simple project and have found myself asking all sorts of questions of the technology which do not seem to be easily and readily supported without resorting to hacking around in HTML. One such example came up recently where I needed to default the value for a column upon creating a new Company record. After posting on the forums I was directed to a ‘sample’ which actually showed me nothing even though the forum poster had responded with an answer that was meaningful. I thus decided to document the actual methodology so that you don’t have to!

In short I have a Responder object which upon creation needs the ‘ResponderType’ property to be primed with the value ‘2’ to indicate what type of responder we are creating. I have included a full listing  of the ‘MVC’ components although only the items highlighted in bold red are actually pertinent to this blog post. The rest is provided for completeness (although as the code has been derived in reality from some abstracted classes there may be some errors, so apologies in advance).

The controller code is as follows with the pertinent code residing in SetUpResponderGrid where we declare a client side javascript handler that we will utilise to intercept the load of the ‘Add Form’ only.

public class ResponderController : BaseController{

protected IResponderService responderService;

protected override string entityName {get { return “Company”; }}

public ResponderController(IResponderService svc) {

responderService = svc;

}

 

public ActionResult Maintain() {

ResponderGridModel gridModel = CreateGrid();

var kpiGrid = gridModel.ResponderGrid;

SetUpResponderGrid(kpiGrid);

return View(gridModel);

}

 

protected JsonResult GetAll(ResponderTypes respondersType) {

var gridModel = CreateGrid();

SetUpResponderGrid(gridModel.ResponderGrid);

IQueryable<ResponderModel> responders  = responderService.GetAllRespondersOfType(respondersType).AsQueryable(); 

return gridModel.ResponderGrid.DataBind(responders);

}

 

protected virtual void SetUpResponderGrid(JQGrid grid) {

 grid.Columns.Insert(0, new JQGridColumn {

EditActionIconsColumn = true,

EditActionIconsSettings = new EditActionIconsSettings {

SaveOnEnterKeyPress = false,

ShowEditIcon = true,

ShowDeleteIcon = true

},

HeaderText = “Edit Actions”,

Width = 10

});

var renameCols = grid.Columns.Where(c => c.HeaderText.Contains(“{0}”));

if (renameCols != null) {

foreach (var col in renameCols) {

col.HeaderText = string.Format(col.HeaderText, entityName);

}

}

grid.AutoWidth = true;

grid.Height = Unit.Percentage(100);

 

grid.ToolBarSettings.ShowRefreshButton = true;

grid.ToolBarSettings.ShowAddButton = true;

grid.ToolBarSettings.ShowEditButton = true;

grid.ToolBarSettings.ShowDeleteButton = false;

 

grid.ClientSideEvents = new ClientSideEvents() {

AfterAddDialogShown = “AfterAddDialogShown”,

AfterEditDialogShown = “AfterEditDialogShown”,

BeforeAddDialogShown=”BeforeAddDialogShown”,

};

grid.AddDialogSettings = new AddDialogSettings {

CloseAfterAdding = true,

ReloadAfterSubmit = true,

Resizable = true,

Modal = true, 

};

grid.EditDialogSettings = new EditDialogSettings {

CloseAfterEditing = true,

ReloadAfterSubmit = true,

Resizable = true,

Modal = true,

};

grid.DeleteDialogSettings = new DeleteDialogSettings() {

Modal = true,

ReloadAfterSubmit = true,

Resizable = true,

};

}

protected override ResponderGridModel CreateGrid() {

return new ResponderGridModel();

} 

}

Here is the Grid Model class which contains no pertinent code but is included for completeness.

public abstract class ResponderGridModel {

public ResponderGridModel() {

ResponderGrid = new JQGrid() {

Columns = new List<JQGridColumn>() {

new JQGridColumn(){
DataField=”ResponderID”,
PrimaryKey=true,
Editable=false,
Visible=false,
},

new JQGridColumn(){

NullDisplayText=”Please enter a (0) name”,
DataField=”ResponderName”,
HeaderText=”{0} Name”,
Width=100,
EditDialogRowPosition=1,
EditDialogColumnPosition=1,
Editable=true,

},

new JQGridColumn(){

NullDisplayText=”Please enter a {0} code”,
DataField=”ResponderCode”,
HeaderText=”{0} Code”,
Width=50,
EditDialogRowPosition=2,
EditDialogColumnPosition=1,
Editable=true,

},

new JQGridColumn(){

DataField=”BOID”,
Editable=true,
Visible=false,

}’

new JQGridColumn(){

DataField=”ResponderType”,
Editable=true,
Visible=false,

},

new JQGridColumn(){

DataField=”UpdateID”,
Visible=false,
Editable=true,

}

},

Width = Unit.Pixel(640),
Height = Unit.Percentage(100),

};

}

public JQGrid ResponderGrid { get; set; }

}

And finally here is the RAZR view which contains the javascript function that we declared in the server side code. This code finds the text controls that we need to initialise and does so with the values that we require.

@using Trirand.Web.Mvc
@using COCO.XXX.Models.Responder;
@model COCO.XXX.Models.Responder.HEIGridModel
@{

ViewBag.Title = “Maintain Companies”;
Layout = null;

}

<!DOCTYPE html>
<html>

<head>
<title>@ViewBag.Title</title>

<link rel=”stylesheet” type=”text/css” href=”http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.0/themes/redmond/jquery-ui.css&#8221; />
<link rel=”stylesheet” type=”text/css” href=”http://www.trirand.net/aspnetmvc/Content/themes/ui.jqgrid.css&#8221; />
<script src=”http://ajax.microsoft.com/ajax/jquery/jquery-1.9.0.min.js&#8221; type=”text/javascript”></script>
<script type=”text/javascript” src=”http://www.trirand.net/aspnetmvc/Scripts/trirand/i18n/grid.locale-en.js”></script&gt;
<script type=”text/javascript” src=”http://www.trirand.net/aspnetmvc/Scripts/trirand/jquery.jqGrid.min.js”></script&gt;
<link href=”@Url.Content(“~/Content/Site.css”)” rel=”stylesheet” type=”text/css” />
<link href=”@Url.Content(“~/Assets/css/site.css”)” rel=”stylesheet” type=”text/css” />
<link rel=”stylesheet” href=”@Url.Content(“~/Assets/css/bootstrap.min.css”)” />
<link rel=”stylesheet” href=”@Url.Content(“~/Assets/css/bootstrap-responsive.min.css”)” />
<script src=”@Url.Content(“~/Scripts/jquery.jqGrid.js”)” type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/jquery.jqGrid.min.js”)” type=”text/javascript”></script>
</head>
<body>

<script type=”text/javascript”>

function AfterAddDialogShown() {

var TypeCol = $(“#tr_ResponderType”);
TypeCol.attr(“hidden”, “true”);

var responderType = $(“#ResponderType”)
responderType.val(“2”);

}

function AfterEditDialogShown() {

var TypeCol = $(“#tr_ResponderType”);
TypeCol.attr(“hidden”, “true”)

}

</script> 

<div>

@Html.Trirand().JQGrid(Model.ResponderGrid, “CompanyGrid”);

</div>

</body>

</html>

I suspect I shall be publishing many more articles over the coming months with regard to the JQGrid as I am just getting to grips with it in a working environment and I find myself  disappointed with the amount of documentation available specifically for the .NET MVC implementation.