Inserting and Updating List Items with ASP.Net MVC and JQuery

In ASP.Net MVC you can display lists of data either as a grid with paging, just as in ASP.Net, or, as I prefer most often, using Infinite Scrolling.

Editing and item or adding an item to the list needs a different approach in Infinite Scrolling as opposed to the paging grid: after adding or updating, you do not want the user to have to scroll back to the original location of the record inside the grid.

Let’s have a look at how Infinite Scrolling can be implemented:

$.get("/More/Items/" + page /* + other options, such as sort order */ , 
  function(data) {    if (data != "") {
      $("#myList").append(data);
    }
  }
);

So we need a controller method returning a partial view filled with the data model of the list.

To support adding a newly created record, or displayed an updated record in the list, we split the list’s partial view into an enumeration part and an item part

List.ascx:

<table><thead><tr><th>.....</th></tr></thead><tbody>
<% 
  foreach(var item in Model.Items) {
    Html.RenderPartial("ListItem", item);
  }
%>
</tbody></table>

The ListItem.ascx then contains the record inside a <tr>:

<tr><td>.....</td>....</tr>

Easy.

Next. we want to support adding and editing records. Personally I prefer jQuery UI’s $.dialog() to open a form for editing the record, and upon closing the dialog, the data is stored using an Ajax or Web service request, and finally the list item is updated:

var tbl = $("#myTable tbody");

$.get('Ajax/GetInsertedRecord?id=' + data.Id
  function (data) {
    tbl.prepend(data);    // insert as first record in list
  }
);

where data is the record returned from the Ajax call to insert the data.

Analogously for updating the edited record, use

var tr = hl.parent().parent();  // retrieve the <tr> of the Edit button

$.get('Ajax/GetUpdatedRecord?id=' + data.Id,
  function (data) {
    tr.replaceWith(data);
  }
);

This in-place adding and updating just require 2 controller methods, and the record partial view extracted from the original list view! Both controller methods return the same partial view as the one referenced in the list partial view.

Typescript Support for JavaScript Libraries such as jQuery

Most JavaScript code relies on functionality implemented by the numerous JavaScript libraries and frameworks available, such as jQuery.

To use these libraries from Typescript, you can either create so-called ambient definitions for the JS library manually, or use existing Typescript definition files (extension .d.ts).

The TypeScript source code repository contains a jquery.d.ts in the /bin directory, and a definition file for jquery.ui can be found in /samples/warship. However, those samples need not cover the respective library completely.

This question on SO points us to the github repository DefinitelyTyped

TypeScript type definitions repository for popular JavaScript libraries.

The project aims to provide high quality definitions for the most popular libraries out there.

which lists about 50 definition files covering major JS libraries and frameworks.

Grids, Paging and Infinite Scrolling

In Good Old ASP.Net, if you wanted to display a large data set, you chose a DataGrid or a GridView, connected it to a DataSet, and you got paging and sorting with a couple of clicks. If you wanted something fancy, like Ajax-style partial postbacks, there was the UpdatePanel. And if you wanted to achieve database-side paging and sorting (rather than C# = client-side), you needed to add some magic to get it to work.

Things change as we move on the ASP.Net MVC. The simplest way to display data is to have a foreach loop iterating a List<T> or array T[], and render the <tr>’s or <div>’s for each record. The MvcContrib project on CodePlex contains a Grid helper for every version of MVC. As of MVC 3, there is a WebGrid (blog, blog, blog) which also implements paging and sorting.

Server-side paging again is not the default functionality, but can be implemented with a couple lines of code, as illustrated on MSDN and SO. (The clarify the term “server-side”: it means that the database query returns only the records resulting from paging and sorting parameters. In case of a web application, even though running on a web server, the code that retrieves the database result is considered to be the client, as in “database client”)

But hey, the web moves on, and paging is out, and infinite scrolling is the new hype UI paradigm. Even though I already knew it from Google image search, Twitter, WordPress, or Facebook, I became aware that there was a term for it from the Coding Horror blog. Of course, the new approach has its drawbacks, and the blog also lists a couple of them, and not everybody likes it, at least not everywhere.

Infinite Scrolling also has some “funny” consequences, as stated in this comment:

Personally, I dislike the feature, but it does work better on some sites than others. Pinterest and Twitter use the technique appropriately. Facebook on the other hand, does not. Why do I say that? Have you tried to access the links they place at the very bottom of the page? It’s impossible when content is being continually added, pushing those links out of view time and time again.

Therefore, no footer on endless pages!

To implement infinite scrolling in ASP.Net MVC, there are already a couple of libraries available: Infinite Scroll on CodePlex (blog), the Endless Scroll (jquery archive, github, blog) and Infinite Scroll (web, github, sample,) jQuery Plugins, or custom jQuery code (sample, demos).

While researching this post, I also came across a hybrid of pagination and infinite scroll:

So here’s a hybrid solution that still separates content into pages and loads them when you scroll down but also allows you to skip around.

Page links are inserted between the content that’s been loaded and appended.

This solves multiple problems:

  • Separates content into pages so users know how “far” they are into your site and can find his/her way back to their favorite content by remembering the “page” they were last on.
  • No more linear flow, users can skip pages if they know the content they’re looking for is much further in.
  • If you skip pages they won’t load, saving time and bandwidth.
  • If you did skip pages, you can go back and load them by clicking on their page number.

 

Fixing jQuery Validation with changing validators

Got jQuery Validation running, and now I know that it requires every input and select to have a name property; otherwise it will simply ignore your validation rules:

jquery.validate.js line 130:
staticRules[element.name] = existingRules;

It will however still apply the CSS-based rules, so finding that out was a bit tricky.

Next stop: I create a dialog with a dropdown to select some sort of object type, each object type having different attributes and different validation rules.

Of course, the validation rules have to be re-defined every time the dropdown value changes. If there is already a validator defined on the form, you need to destroy the existing validator

var form = $('#myForm).get(0);
$.removeData(form, 'validator');

(found here)

Still, some mysterious bit of code still remains, as validation is still executed even though the validator had been deleted and the dropdown is ignored in the (non-existing?) validator:

Uncaught TypeError: Cannot read property 'settings' of undefined
 jquery.validate.js:315

says Chrome, and IE reports

Unable to get value of the property 'settings': object is null or undefined

The culprit is the method delegate() (line 313), which does not check whether a validator exists:

function delegate(event) {
  var validator = $.data(this[0].form, "validator"),
  eventType = "on" + event.type.replace(/^validate/, "");
  // this fixes handling the deleted validator:
  if (!validator) return;
  validator.settings[eventType] && validator.settings[eventType].call(validator, this[0], event);
}

Validating a jQuery Dialog using jQuery Validate in ASP.Net

After I managed to create and display a jQuery dialog using the $().dialog() function, I wanted to replace my ad-hoc validation code with a “real” validation plugin, namely jQuery Validation. (naming consistency of jQuery plugins did not seem to matter until now, as there are several plugins with similar names in this area)

As much as I tried, the validator’s valid() would always return true, and debugging revealed that there were no elements to be checked, even though I had declared them in the validate() method.

Finally I came across this answer on SO, stating

the jQuery-UI dialog does not append to the form, it appends just before </body>, so the elements to validate are outside the <form></form> section

and suggesting the solution

$("#mydiv")
  .dialog("open")
  .parent()
  .appendTo(jQuery("form:first"));

Now it was obvious what’s happening: the dialog() function moves the <div> outside ASP.Net’s default form element (id=’aspnetForm’), directly under the <body> element.

Since I want to have several jQuery dialogs in my ASP.Net page, and cannot freely add <form> tags in the source code (especially not in .ascx and not in .aspx inside a MasterPage), I decided to create a <form> on the fly, and open the dialog inside the new form:

if (!$("#myForm").length) {
  $("<form>")
    .attr("id", "myForm")
    .attr("name", "myForm")
    .appendTo($("body")); 
} 

var d = $("#myDialog").dialog({ 
  autoOpen: false, 
  modal: true, 
  open: function () { 
    $("#myForm").validate({ ... }).resetForm(); 
  }, 
  buttons: [ 
    { id: "OK", 
      click: function() { 
        if (!$("#myForm").valid()) 
          return false; 
        ... process data ... 
        $("#myDialog").dialog("close"); 
      } 
    } 
  ] 
}); 

d.dialog("open")
  .parent().appendTo($("#myForm"));

Note that I added the call to resetForm() to clear errors from a previous execution of the dialog.

You can call the form validation using the .valid() method and simply leave the dialog open if a validation error occurred.

Have a look at the demos for help regarding HTML and CSS declarations.