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

May 16, 2013

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.


What is ‘this’? Confusion in JavaScript and Typescript

December 23, 2012

Since Typescript supports “true” object-oriented programming using the ‘class’ and ‘interface’ keywords, there seems to be an expectation that the semantics of JavaScript is suspended and replaced by Something Completely New. This is not the case!

Remember, Typescript is a syntactic extension of JavaScript and compiles to JavaScript. The underlying concepts remain unchanged. (Un)fortunately (depending on your point of view and your expectations), this is also true for the ‘this’ keyword.

Let’s have a look at some StackOverflow questions regarding the meaning of ‘this’:

The questions assume that a callback function passed to another function will be executed in the context of the original object, therefore retaining the semantics of ‘this’ identified the original object:

myFoo(){
    otherFoo.otherBar(this.myBar);
}

In JavaScript, however, the function myBar is executed with ‘this’ referring, depending on the implementation of otherBar, to the otherBar function, or something completely different.

Do not despair, though, there are a couple of possible solutions:

 

 


Typescript Support for JavaScript Libraries such as jQuery

November 15, 2012

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.


Installing TypeScript in Visual Studio 2010

November 9, 2012

After my lengthy introduction on how I finally got interested in developing in JavaScript and in the promises of TypeScript, here is how to install TypeScript in Visual Studio 2010.

The TypeScript homepage only refers to installing TypeScript in VS 2012, and the MS download page states

TypeScript for Visual Studio 2012 will install when Visual Studio 2012 is not installed, and provide the TypeScript compiler.

However, this answer on SO indicates a solution for VS2010:

  • Download the installer package (most current is TypeScriptSetup.0.8.0.msi)
  • Run the installer package to install the TypeScript compiler
  • Extract the .msi using 7zip
  • Copy or rename the extracted TypeScriptLS.vsix_File to TypeScriptLS.vsix
  • Run the .vsix file
  • The installer states it will install TypeScript support for VS 2010
  • (Close and) Open Visual Studio 2010

After successful installation, Visual Studio provides:

  • a new project type named “HTML Application with TypeScript”
  • a new item type named “TypeScript File”

If you do not find the new types immediately, simply type “Type” in the search box of the Add New dialog.


JavaScript Love Hate

November 8, 2012

I have to admit: in all those years of web development, I was quite reluctant to write any JavaScript code.

In the early days of ASP.Net, JS was either not required or not (easily) possible to implement the functionality we know today. This changed with the arrival of the Ajax Control Toolkit that simply injected pre-built pieces of JavaScript code into a page using server-side controls, or by manually typing a couple lines of code.

The main reason for me, though, was that you cannot compile JavaScript.

You have no idea whether your piece of code actually works – you need to run it to see whether it’s ok. Combine this with the typical behavior of IE on a dev machine, popping up a message box whenever a JS error occurs, or different parsers with different sensitivities to well- and mal-formedness of code in different browsers (missing a semicolon, adding a comma too much), and it’s easy to see that for people used to compiling code there was a lot to dislike.

Also, until quite recently, there were simply too many JavaScript libraries available to make a sensible choice between jQuery, prototype, ext-js, and several others. Fortunately (or unfortunately, depending on your preferences), this has changed, as illustrated in this chart.

But the times, they are a-changing. Today’s web applications are inconceivable without proper client-side functionality and asynchronous server communication, all implemented in Javascript. As Scott Hanselman cited more than a year ago, “JavaScript is Assembly Language for the Web“.

Why do I write all this? Well, I finally brought myself to write my first JavaScript application. The ASP.Net MVC project is essentially just the container for JS code, and also implements the Ajax backend using Controllers that return JsonResults.

While programming in JavaScript “for real”, I got quite fascinated about the things that are possible in JavaScript, and the project soon reached 2000 lines of code. However, as it goes with first projects, the code soon became unmanageable and needed a cleanup. The code implemented various dialogs with partially dynamic fields and validation, a hierarchical database browser and a web directory browser, but it was a mess.

The first restructuring was to avoid global variables as much as possible, encapsulate the Ajax calls in a static object, and each dialog in one function, with the event handlers implemented as local function variables.

But all this code with its anonymous parameters and asynchronous callbacks passing untyped (in JavaScript, but typed in C#) parameters to the success() functions is still difficult to understand (“write-only”).

The latest innovation that gave me a prospect of typed, compilable, and modular programming is called TypeScript, an extension to the JavaScript language that compiles to JavaScript (CodePlex). It installs as new project type and item type in Visual Studio, and its support for typical IDE functionality such as Intellisense, Go to Definition, Find References, etc. is quite good, judging from first glance.

Finally, a couple of links to introductory news articles that drew my interest for TypeScript:

Give it a try!


Selecting an OpenID Selector

August 2, 2012

As soon as you get started with OpenID, you face the question of which OpenID Selector to choose. There are so many!

Project Latest Version Comments
openid-selector 11-02-10 ~10.000 downloads
openid-ps 10-01-10 imitates StackOverflow UI, only 200 downloads
jQuery OpenID plugin 09-02-10 “inspired” by openid-selector, openid-realselector
openid-realselector 09-04-29 no downloadable .zip

Now that I collected the data, the choice seems clear: use openid-selector, and check the modifications included in the openid-ps project.


Left-to-Right Tab Order in CRM 2011 Form

July 25, 2012

Looking for a solution to set the tab order in a CRM 2011 Form to tab horizontally (rather than vertically), I found a couple of solutions (such as here and here).

The solutions, however, were not complete, and required a bit of tweaking:

  • including the tabs for headers inside a form
  • setting the tabIndex higher than the navigational tab indexes

Finally, my function looks like this:

function TabOrderLefttoRight() {
    for (var i = 0; i < crmForm.all.length; i++) {
        var element = crmForm.all[i];
        if (element.tabIndex && element.tabIndex != "0") {
            if (element.className == 'ms-crm-Hidden-NoBehavior') 
                continue;
            if (element.tagName == 'A') {
                if (element.className != 'ms-crm-InlineTabHeaderText') 
                    continue;
            }

            element.tabIndex = 10000 + (i * 10);
        }
    }
}

Simply add a call to TabOrderLeftToRight() in your form’s onLoad event.


Validating a jQuery Dialog using jQuery Validate in ASP.Net

July 16, 2012

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.


Thou Shalt Not Update on GET

June 27, 2012

Just ending a bug-fixing session of an ASP.Net project, we came across an interesting issue:

  • The customer wanted to avoid the “post again” message when pressing the browser’s back button (scenario sketched here)
  • The web application used templates file to inject HTML code into the pages as a means to allow localization and customization by the customer
  • Some of the post-back actions should be triggered by links or buttons contained in the template files
  • Posting by mechanisms of the ASP.Net framework and implementing the Post/Redirect/Get design pattern was therefore not possible
  • Manipulation of the web site’s shopping cart had therefore been implemented as links issuing GET requests

As a consequence, when you added a product and hit the Back button to view the product again, you added it once more to the shopping cart. Clearly, this is in violation of the accepted semantics of POST and GET requests:

In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered “safe”.

Not good, greetings from The Spider of Doom.

To avoid major reorganization and rewrites of the existing pages, I took a shortcut using Javascript:

function PostAndRedirect(baseurl, params) {
  $.ajax({
    type: "POST",
    url: baseurl + params,
    success: function (xhr) {            // Success
      window.location.href = baseurl;
    },
    // If any AJAX errors, alert them
    error: function (xhr, ajaxOptions, thrownError) { 
      alert(xhr.status);
      alert(thrownError);
    }
  });
}

The function PostAndRedirect takes as parameter the target (most likely current) URL and the parameters to be posted. The POST is executed using the jquery.ajax() method. If the POST is successful, the function redirects to the URL (i.e. using a GET request).

Using this function, the browser never adds the POST request to its history, and therefore would not re-post upon pressing the Back button.

To invoke the Javascript function, simply add a Javascript URL as href value:

<a href="javascript:PostAndRedirect('SomeUrl.aspx', 
    '?cmd=add&productid=SomeProductId');">

Computer says “fakepath”

April 2, 2012

In a web application, we originally implemented the following functionality:

  • a user is allowed to upload files (Word documents, PDFs, etc)
  • if the uploaded file is from a network share (mapped drive), the mapped drive path needs to be translated into a UNC path
  • using the UNC path, a server component can check for changed file dates
  • if a changed file is detected, some workflow should be initiated

Files uploaded with IE (6 and 7) automatically included the path information of the file, whereas for Firefox 3, no file path was passed. This was worked around with a bit of JavaScript:

    ClientScript.RegisterClientScriptBlock(GetType(), "copy",
@"function copyName(){
    document.getElementById('" + edFullFilename.ClientID + "').value = 
        document.getElementById('" + edFilename.ClientID + @"').value;
}", true);
    edFilename.Attributes.Add("onkeyup", "copyName()");
    edFilename.Attributes.Add("onfocus", "copyName()");
    edFilename.Attributes.Add("onchange", "copyName()");

which essentially copied the original file path to a hidden input field.

As browsers are becoming more aware of security, and implement more and more HTML 5 features, all this changes.

IE8 started to introduce the c:\fakepath\ pseudo directory, and other browsers followed. As stated on the WHATWG mailing list,

The original plan was to just have the filename. Unfortunately, it turns out that if you do that, there are certain sites that break, because they expect the path (and they expect a Windows path, no less). This is why Opera and IE8 return a fake path — not because HTML5 says to do it. In fact I made HTML5 say it because they were doing it.

(I would expect Firefox, Safari, and Chrome to follow suit; Firefox for compatibility, and Safari and Chrome for privacy.)

For IE, there remains the solution to add the web server to the Trusted Internet Zone

Additionally, the “Include local directory path when uploading files” URLAction has been set to “Disable” for the Internet Zone. This change prevents leakage of potentially sensitive local file-system information to the Internet.

but we are looking for a generic cross-browser solution.

Probably it’s time to rethink the whole feature and make users copy+paste the file name rather than upload the file for such a scenario.


Follow

Get every new post delivered to your Inbox.