Skip to content
Archive of posts filed under the JavaScript category.

JqGrid, getRowData and Cells in Edit Mode

Today I was fighting with a very specific problem.  I was working with jqGrid’s getRowData method.  I was implementing batch save feature, where a user can edit some data in the grid, then hit Save button to send the data to the server.  I set up a column (SortOrder) to be always editable to be more user friendly.  Here is what the column definition looks like for that column:


{ name: 'SortOrder', width: 100, index: 'SortOrder', align: 'left', search: false, 

editable: true, formattype: 'integer', formatoptions: { disabled: false }  },

Now, in my button click, I am calling getRowData, but instead of actual sort order value in the column, I get the html that defines the input field for SortOrder column.  There is no way around this, according to my research, so I had to manually parse the data out of the input field’s html.  Here is how I did that:

getGridData: function () {
  var gridData = $('#myGrid').jqGrid('getRowData');
  for (var i = 0; i < gridData.length; i++) {
    $('#myGrid').jqGrid('saveRow', gridData[i]['id'], false, 'clientArray');
  }
  gridData = $('#myGrid').jqGrid('getRowData');
  var pattern = /(id=\".+_SortOrder\")/ig;
  for (var j = 0; j < gridData.length; j++) {
     var sortOrder = gridData[j]['SortOrder'];
     if (sortOrder.toLowerCase().indexOf('<input') >= 0) {
         var matched = sortOrder.match(pattern)[0];
         var numberOfInput = matched.toUpperCase().replace("ID=\"", "").replace("_SORTORDER\"", "");
         sortOrder = $('#' + numberOfInput + '_SortOrder').val();
     }
     gridData[j] = {
       Name: gridData[j]['Name'],
       SortOrder: sortOrder
     };
  }
  return gridData;
}, 

Let’s walk through code.  First of all, I am calling saveRow for each row in the grid to save data to memory.  Then I am running through all the rows again, looking at my SortOrder column.  The other column, Name, is read-only.  I am using regular expressions to find HTML  Once this is found, I am parsing out input id.  The reason for that is because if you have more then one row in the grid, any row can have this input control, and it’s id corresponds to the row’s index in the grid.  Once this is done, I am getting the actual value by calling val() function.  Lastly, I am updating the value in the row.  Once this is done, I can use ajax function of jQuery to send the data to the server.  I use JSON.stringify(getGridData()) and then parse it back out on the server side in my controller’s method responsible for saving this data.

That’s all there is to it.

Thanks.

Post to Twitter

Detecting Pending Changes in ASP.NET MVC

In this blog I am going to describe a pretty common problem that web applications have to deal with along with one way to solve this issue.

Here is the issue at hand.  Say, user pulled up an entry form and made some changes.  Then the above mentioned user clicked browser back button, navigating away from the current page, and thus loosing all the pending changes.  A friendly software application should warn the user, shouldn’t it?

So, how do we detect pending changes?  In typical desktop application, say the one that uses CSLA – http://lhotka.net/cslanet/, you can very easily check existing bound objects and see if there are pending changes.  CSLA, for example, takes a snapshot of business objects, then compare the snapshot to current state of the same object.  What if we could do the same?

To our benefit, there is a very useful function in jQuery, called serialize
http://api.jquery.com/serialize/

So, could we use that to solve the problem?  Let’s try something like

initialFormData = $(‘form’).serialize();

Seems pretty easy.  Now we need to create a JavaScript object that can help us to all the steps

  • Create snapshot of the data when it is loaded
  • Compare the snapshot to current data
  • Intercept navigation in progress and warn the user

I am going to create a brand new script file with my application in it, calling it App.js and define my object in it as following

if (!window.App) {
    window.App = {
        init:
function
() {
            window.onbeforeunload =
this
.onBeforeUnloadWindow;
           
this.initialFormData = $(‘form’
).serialize();
           
this.submitting = false
;
        },
        onBeforeUnloadWindow:
function
() {
           
return
window.App.hasPendingChanges();
        },
        submitting:
false
,
        initialFormData:
""
,
        hasPendingChanges:
function
() {
           
if (!window.App.submitting && 
                window.App.initialFormData !== $(
‘form’
).serialize()) {
               
return "You have pending changes. \n\rIf you leave current page, those changes will be lost."
;
            }
           
return
undefined;
        }
    };
}

Very quick and easy.  One small object, neatly organized that will do the work for me.  Now in my MVC application I locate _Layout.cshtml and include my script in the list of scripts.  The last step is to call init method on my App object after form loads, for example in Edit.cshtml:

<script type="text/javascript">
        window.App.init();
</script>

Now, if you try this solution, you will notice that your Save button does something strange.  It pops up the warning message for no reason.  So, we have to do one more thing in our Save button click event:

<input type="submit" onclick="window.App.submitting = true;" 
 
value=
@ViewBag.Action />

We have to let the App object know that we are navigating away because the user hit Save button.  That was our last step.  One of the guys on our team came up with this idea, and he and I refined it a bit to make the code cleaner.

Please let me know what you think.

Thanks.

Post to Twitter

jqGrid ajax Operations in ASP.NMET MVC

I am using jqGrid in my current project, and I already blogged prior on how impressed I am with the functionality available in this control.  I am using it in ASP.NET MVC application, but it can be used in any web app.  One of the key features is that it supports ajax based asynchronous server operations.

Upon my research I stumbled onto this article that greatly simplifies the code required to process search requests in ASP.NET MVS that are generated by the grid

http://www.codeproject.com/KB/aspnet/AspNetMVCandJqGrid.aspx

I had to make a small addition to it though.  The code works great in multi-search scenarios, but if you turn off multisearch option in search options, than you will get an error.  I also remove empty try/catch blocks which I personally find to be a bad practice.  Here is the updated code I used for Filter class:

using System.Runtime.Serialization;
using
System.Runtime.Serialization.Json;
using
System.Text;
 
[
DataContract
]
public class Filter

{
    [
DataMember]
   
public string groupOp { get; set
; }
    [
DataMember
]
   
public Rule[] rules { get; set
; }
 
   
public static Filter
Create(
       
string
jsonData, 
       
string
searchField, 
       
string
searchString, 
       
string
searchOper)
    {
       
Filter returnValue = null
;
       
if (!string
.IsNullOrEmpty(jsonData))
        {
           
var serializer = new DataContractJsonSerializer(typeof(Filter
));
           
using (var ms = new System.IO.MemoryStream(Encoding
.Default.GetBytes(jsonData)))
            {
                returnValue = serializer.ReadObject(ms)
as Filter
;
            }
 
        }
       
else

        {
            returnValue =
                   
new Filter()
                    {
                        groupOp =
"AND"
,
                        rules =
new[] { new Rule
() 
                        { data = searchString, field = searchField, op = searchOper } }
                    };
        }
       
return
returnValue;
    }
}

 

I added else statement that checks the json data variable, which will be null in cases of single field search.  The code will fall into same patch though, which is convenient because you process data code can stay the same whether or not you are using single or multi-search. 

Of course, I had to update the model binder to match:

using System.Web.Mvc;
 

public class GridModelBinder : IModelBinder

{
   
public object BindModel(
       
ControllerContext
controllerContext,
       
ModelBindingContext
bindingContext)
    {
       
var
request = controllerContext.HttpContext.Request;
       
return new GridSettings

        {
            IsSearch =
bool.Parse(request["_search"] ?? "false"),
            PageIndex =
int.Parse(request["page"] ?? "1"
),
            PageSize =
int.Parse(request["rows"] ?? "10"
),
            SortColumn = request[
"sidx"] ?? ""
,
            SortOrder = request[
"sord"] ?? "asc"
,
            Where =
Filter
.Create(
                request[
"filters"
],
                request[
"searchField"
],
                request[
"searchString"
],
                request[
"searchOper"
])
        };
    }
}

 

I want to thank Ilya Builuk for sharing his code.  I hope you will find my enhancements useful.  Another side thought.  There is a string based IQueryable extensions library that you can use to construct queries against this interface based on syntax such as “Where SomeColumn =- 2”.  Not that I am a giant fan of leaking my data access code into controllers or anything.  You can read more about dynamic linq on Scott Guthrie’s blog here

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Thanks.

Post to Twitter

Localizing JavaScript in ASP.NET MVC

This is something I researched recently while working on an ASP.NET MVC application.  My goal was to localize JavaScript messages based on user preferences.  In this post I am going to document the approach I settled on.  I am going to test based on browser culture and also drive the application based on the same preferences.

First of all, here is how I am setting up for testing using Internet Explorer.  Go to Settings menu, then select Internet Options.  You will see Languages button.

 

image

Once I am there, I am going to add Spanish for testing.

image

I can now test by moving preferred language up and down.

Now, in my controller (you can do in the base controller or any other place that can e hit before any code that manipulates the user interface, such as labels or JavScript.  Here is an example from Home controller in a brand new MVC app.  If you add a brand new MVC project, you will see that controller.  You can always create a base class and put your code there, which is what I did.

using System.Globalization;
using System.Threading;
using System.Web.Mvc;

namespace LocalizeJavaScriptMvc.Controllers
{
    public class HomeController : Controller
    {
        private void SetCulture()
        {
            if (HttpContext.Request.UserLanguages != null &&
                HttpContext.Request.UserLanguages.Length > 0)
            {
                Thread.CurrentThread.CurrentCulture =
                    new CultureInfo(HttpContext.Request.UserLanguages[0]);
                Thread.CurrentThread.CurrentUICulture =
                    Thread.CurrentThread.CurrentCulture;
            }
        }
        public ActionResult Index()
        {
            ViewBag.Message = MainResource.Title;

            return View();
        }

        public ActionResult About()
        {
            return View();
        }

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            SetCulture();
        }
    }
}

So, now my main thread has been setup.  Next, I am going to create two resource files. One with code generation, specifically MainResource.resx.  Just Select Add New Item from project menu, and select new Resource, then give it the name as above.  Repeat the step, this type using MainResource.es.resx.  Pull up MainResource in the editor window, add some strings and set it’s access modifier to public:

image

 

Now, to JavScript.  My experience in the language is somewhat limited, but I dutifully read the most recommended book on the language, JavaScript – the Good Parts many months back.  So, I added brand new JavaScript file to my project with my class that will be responsible for localization – mainApp.js. 

if (!window.resourceProvider) {
    window.resourceProvider = {
        message1: ”,
        message2: ”
    };
}

My resourceProvider class has two properties, that I set to empty strings. I like defining properties because I get IntelliSense when I do. In my “master page” – layout page – _Layout.cshtml I need to include my new script

<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/mainApp.js")" type="text/javascript"></script>
</head>

The last step is to populate my message1 property with the actual text. I will do that in the bottom of the view:

    </div>
</body>
</html>
<script type="text/javascript">
    resourceProvider.message1 = ‘@(MainResource.Message1)’;
</script>
Now, I just need to call it from JavaScript. For testing, I am just adding a button ti Index.cshtml and script to invoke Click event with a simple alert:
@using LocalizeJavaScriptMvc
@{
    ViewBag.Title = "Home Page";
}
<h2>@ViewBag.Message</h2>
<p>
    <button id="clickhere">@MainResource.Click</button>
</p>
<script type="text/javascript">
    $().ready(function () {
        $(‘#clickhere’).click(function () {
            alert(resourceProvider.message1);
        });
    })
</script>

As you can see, I am localizing button text and using my resourceProvider class with its message1 property to localize the alert.

To test, I just set preferred language in browser to English and Spanish, and run the app twice to test.

You can download solution at http://dotnetspeak.com/Downloads/LocalizeJavaScriptMvc.zip

Please let me know if you have any comments regarding this solution.

Thanks.

Post to Twitter