jqGrid and ASP.NET MVC - Formatting

Last time we have set up our first jqGrid. In this post I'm going to take a look at columns formatting.
First let's use predefined formatter. Setting any of predefined formats for column is as simple as adding formatting type and options to colModel definition. In our example we will format UnitPrice as USD. To achieve this, we will set formatting type to currency with following options: decimalSeparator: '.', thousandsSeparator: ',', decimalPlaces: 2 and prefix: '$'.
<script type="text/javascript">
  $(document).ready(function() {
    $('#jqgProducts').jqGrid({
      ...
      colModel: [
                  ...
                  { name: 'UnitPrice', index: 'UnitPrice', align: 'left', formatter: 'currency', formatoptions: { decimalSeparator: '.', thousandsSeparator: ',', decimalPlaces: 2, prefix: '$'} },
                  ...
                ],
      ...
    });
  });
</script>

Now let's take a look at custom formatter. We will prepare formatting javascript function for UnitsInStock column (instead of displaying numbers we will be displaying flags which color will depend on amount of units). Function receives three parameters: cellvalue - original value for cell, options - array of options passed in colModel, rowObject - object with row data. Return value of our function should be new value for a cell. We also have to set formatting in colModel.
<script type="text/javascript">
  $(document).ready(function() {
    $('#jqgProducts').jqGrid({
      ...
      colModel: [
                  ...
                  { name: 'UnitPrice', index: 'UnitPrice', align: 'left', formatter: 'currency', formatoptions: { decimalSeparator: '.', thousandsSeparator: ',', decimalPlaces: 2, prefix: '$'} },
                  { name: 'UnitsInStock', index: 'UnitsInStockPrice', align: 'left', formatter: unitsInStockFormatter }
                ],
      ...
    });
  });

  function unitsInStockFormatter(cellvalue, options, rowObject) {
    var cellValueInt = parseInt(cellvalue);
    if (cellValueInt > 10)
      return "<img src='../../Content/images/ui-flag_green.png' alt='" + cellvalue + "' title='" + cellvalue + "' />";
    else if (cellValueInt > 0)
      return "<img src='../../Content/images/ui-flag_blue.png' alt='" + cellvalue + "' title='" + cellvalue + "' />";
    else
      return "<img src='../../Content/images/ui-flag_red.png' alt='" + cellvalue + "' title='" + cellvalue + "' />";
  };
</script>

Let's take it a little bit further. We will format Supplier column so that it will include a link that displays a popup with supplier details. To achieve this we will use jQuery UI Dialog. To do that, we must reference jQuery UI script file.
<head>
  ...
  <script src="/Scripts/jquery-ui-1.7.2.custom.min.js" type="text/javascript">
  </script>
  ...
</head>
In Controller we will prepare method returning supplier details.
/// <summary>
///
Provides json data for supplier popup
/// </summary>
/// <param name="supplierId">
supplier identifier</param>
/// <returns>
json data</returns>
public ActionResult SupplierData(int supplierId)
{
  //Getting supplier from repository
  Supplier supplier = _repository.GetSupplier(supplierId);

  //Preparing anonymous variable with json data
  var supplierData = new
  {
    companyName = supplier.CompanyName,
    address = supplier.Address,
    postalCode = supplier.PostalCode,
    city = supplier.City,
    country = supplier.Country,
    phone = supplier.Phone,
    homePage = supplier.HomePage
  };

  //Returning json data
  return Json(supplierData);
}

We also must add html markup for dialog.
<div id="jqdlgSupplier">
  <span id="sCompanyName"></span><br /><br />
  <span id="sAddress"></span><br />
  <span id="sPostalCode"></span>, <span id="sCity"></span><br />
  <span id="sCountry"></span><br /><br />
  <span id="sPhone"></span><br />
  <span id="sHomePage"></span>
</div>

At the end we will write two new javascript functions (one for cell formatting and one for dialog display) and add some more initialization logic.
<script type="text/javascript">
  $(document).ready(function() {
    $('#jqgProducts').jqGrid({
      ...
      colModel: [
                  ...
                  { name: 'Supplier', index: 'SupplierID', align: 'left', formatter: supplierFormatter },
                  ...
                  { name: 'UnitPrice', index: 'UnitPrice', align: 'left', formatter: 'currency', formatoptions: { decimalSeparator: '.', thousandsSeparator: ',', decimalPlaces: 2, prefix: '$'} },
                  { name: 'UnitsInStock', index: 'UnitsInStockPrice', align: 'left', formatter: unitsInStockFormatter }
                ],
      ...
    });
    $('#jqdlgSupplier').dialog({ autoOpen: false, bgiframe: true, resizable: false, title: 'Supplier' });
  });

  ...

  function supplierFormatter(cellvalue, options, rowObject) {
    return "<a href='' onlick='showSupplierDialog(this, " + cellvalue.substr(1, 1) + "); return false;'>" + cellvalue.substr(4) + "</a>";
  };

  function showSupplierDialog(linkElement, supplierId) {
    //request json data
    $.getJSON('/Home/SupplierData/', { supplierId: supplierId }, function(data) {
      //set values in dialog
      $('#sCompanyName').text(data.companyName);
      $('#sAddress').text(data.address);
      $('#sPostalCode').text(data.postalCode);
      $('#sCity').text(data.city);
      $('#sCountry').text(data.country);
      $('#sPhone').text(data.phone);
      $('#sHomePage').text(data.homePage);
      //get link position
      var linkPosition = $(linkElement).offset();
      //set dialog position
      $('#jqdlgSupplier').dialog('option', 'position', [linkPosition.left, linkPosition.top]);
      //open dialog
      $('#jqdlgSupplier').dialog('open');
    });
  };
</script>

And voila. We can seat and admire our grid.

There is one more thing which needs to be mentioned. If you want custom formatted column to be editable (I'm going to write about CRUD scenario soon), you need to provide unformatter function which will extract original value from cell.
As usual, example source code can be found here.

12 comments:

Asystent said...

Great! Nice to see you here! Keep going!

Anonymous said...

quite interesting article. I would love to follow you on twitter.

Anonymous said...

When I click a link, the dialog only shows for a second then disappears. It seems to disappear because of the post back. Any suggestion on how to fix this?

Tomasz Pęczek said...

Does it happens in this sample application or in some yours code?

Tomasz Pęczek said...

I have checked into the repository small modification:
https://tpeczek.svn.codeplex.com/svn/trunk/MVC/jqGridExample/jqGridExample/Views/Home/Formatting.aspx
It makes few changes to javascript for formatting cell and displaying popup:

function supplierFormatter(cellvalue, options, rowObject) {
  return "<a href='#' onclick='showSupplierDialog(this, event, " + cellvalue.substr(1, cellvalue.indexOf(']') - 1) + ");'>" + cellvalue.substr(cellvalue.indexOf(']') + 2) + "</a>";
};

function showSupplierDialog(sender, e, supplierId) {
  //cancel the click event
  if (e.preventDefault)
    e.preventDefault();
  else
    e.returnValue = false;
  //request json data
  $.getJSON('/Home/SupplierData/', { supplierId: supplierId }, function(data) {
    //set values in dialog
    $('#sCompanyName').text(data.companyName);
    $('#sAddress').text(data.address);
    $('#sPostalCode').text(data.postalCode);
    $('#sCity').text(data.city);
    $('#sCountry').text(data.country);
    $('#sPhone').text(data.phone);
    $('#sHomePage').text(data.homePage);
    //get link position
    var linkPosition = $(linkElement).offset();
    //set dialog position
    $('#jqdlgSupplier').dialog('option', 'position', [linkPosition.left, linkPosition.top]);
    //open dialog
    $('#jqdlgSupplier').dialog('open');
  });
};

This should made event cancelation crossbrowser compatible.

Emily Johnson said...

quite interesting article. I would love to follow you on twitter.

Anonymous said...

Nice article.In this sample supplier popup will not open.

Tomasz Pęczek said...

Are you refering to this three years old code in post (which is outdated) or about the updates in comments and at the repository?

Anonymous said...

yes.Am new to mvc applications...so jus start with mvc1....When i try to use this sample everything is working fine(including crud operations....)But the popup is not opening...

I ll give my code below..correct me if i am wrong.....sorry for trouble

colModel: [
{ name: 'Description', index: 'Description', align: 'left', width: 200, formatter: descriptionFormatter, unformat: descriptionUnFormatter, editable: true, edittype: 'textarea', editoptions: { size: 80, rows: "2", cols: "20" }, editrules: {required: true} }

],



$('#jqdlgDescription').dialog({ autoOpen: false, bgiframe: true, resizable: false, title: 'Description' });

function descriptionFormatter(cellvalue, options, rowObject) {


return "" + cellvalue.substr(cellvalue.indexOf(']') + 2) + "";


};

function descriptionUnFormatter(cellvalue, options, cellobject) {
return cellvalue;
}
function showDescriptionDialog(linkElement, PropertyID) {

//cancel the click event
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;

//request json data
$.getJSON('/Property/DescriptionData/', { PropertyID: PropertyID }, function(data) {
//set values in dialog
$('#sDescription').text(data.Description);

//get link position
var linkPosition = $(linkElement).offset();
//set dialog position
$('#jqdlgDescription').dialog('option', 'position', [linkPosition.left, linkPosition.top]);
//open dialog
$('#jqdlgDescription').dialog('open');
});
};

Tomasz Pęczek said...

Hi,

I'm afraid that comment limitations has strip some of your code.

Now both, this code and ASP.NET MVC 1 are so outdated (we have ASP.NET MVC 4 now) that in general I no longer support it. You should try playing with ASP.NET MVC 3 version of this samples as it has a lot of fixes: http://tpeczek.codeplex.com/releases/view/61796

Anonymous said...

Thanks for ur reply....

Anonymous said...

Great Article!!!!!

Need to send an EMail when click on UI Dialog submit button in JQGrid.
Can u tel me how to do this?

Thank u