Simple Talk is now part of the Redgate Community hub - find out why

Revisiting Partial View Rendering in ASP.NET MVC

For any browser-based application, it makes sense to load into the web page just the content that is immediately required and avoid whole-page refreshes whenever possible. Ajax technology and JSON makes this partial-rendering easy. It is, however, worth considering ASP.NET's own partial-rendering techniques, returning HTML. It requires less client-logic and is quicker to implement.

In the real-world, or at least in the section of the real world that I see every day, there are still plenty of classic ASP.NET Web Forms and ASP.NET MVC web sites. By ‘classic web sites’ mean web sites where the vast majority of pages are being served entirely from the web server and fully refreshed after each postback. The full refresh of the page after a postback can be significantly slow and cumbersome for users, especially when the use of graphics of these sites is quite heavy. This is the reason why at some point Ajax and partial rendering of pages became so popular.

Today, the Ajax approach is often taken to the limit when single-page applications (SPA) are built. A single-page application (or just a few-pages application) downloads from the web server as an empty DIV element. All the remaining content is inserted dynamically after placing additional HTTP requests for JSON data. In this context, posting a form is merely a JavaScript-driven request. The response of such a request is mostly JSON data that the local script will process as appropriate, updating the user interface. As you can easily appreciate, the performance problem of the full page refresh just doesn’t exist anymore.

In between SPAs and classic full page refresh solutions, there are techniques based on plain jQuery calls to HTTP endpoints returning JSON data. Technically speaking, a jQuery solution is not that much different from anything you can do in a SPA; However, it is a technique that you can use just where required and appropriate but not necessarily throughout the entire solution. You can use jQuery to make a GET request and receive JSON data. Such data is then incorporated in the current DOM via HTML templates and libraries such as Knockout or AngularJS. You can also use jQuery to place a POST request and receive back a plain ack message or some JSON data.

All this is well-known and, for the most part, it is mainstream practice today. In this article, instead, I’ll discuss a different approach for partial page refreshes that is closer to what in ASP.NET Web Forms and ASP.NET MVC is still referred to as ‘partial rendering’. The idea behind partial rendering is that of placing a jQuery call, having the endpoint perform its command or query and then returning any response as pure HTML. Some SPA purists may dislike this approach because-they may say-returning HTML is less efficient than returning plain JSON data. True, but the techniques presented in this article are a lot smoother to implement in coding scenarios where the predominant skills are server-side ASP.NET Web Forms and ASP.NET MVC. Still a bit of JavaScript is required, but it is limited to using familiar DOM properties such as innerHTML or just a few core jQuery methods.

Setting Up a Sample Project

Let’s say you have an ASP.NET MVC project with a Razor view that produces the output of Figure 1. The page contains a list of data elements that users can delete one by one by clicking a button. In classic ASP.NET, you may have a HTML form all around the list and each of the delete buttons is implemented as a submit button. When any button is clicked, the page posts back. On the server you figure out which button was clicked, and from there you get the ID of the element to delete.

2118-clip_image002.jpg

Here’s some code that illustrates the behavior of the ASP.NET MVC endpoint reached when any of the buttons in Figure 1 are clicked.

The method DeletePost proceeds with the backend operation and then redirects to the method in charge of refreshing the view. This is an instance of the Post-Redirect-Get (PRG) pattern that keeps commands distinct from queries and also avoids the nasty problem of resending POST data when the user hits F5 from the browser. Here’s some sample code for the Index method.

The Index method gets the updated list of customers (after the deletion) and refreshes the page. When the PRG pattern is used to implement a POST, the last action repeatable by the browser is always a GET. As a result, users will see the updated list of customers. Even if they press F5 or Refresh within the browser all they obtain is a plain refresh of the page; no additional deletion of data occurs.

How can this code be improved to avoid a full page refresh without rewriting the entire application, or large portions of it, as a SPA? A possible approach is based on the HTML Message pattern-one of the most popular Ajax patterns. The HTML Message pattern refers to a situation in which the request carries back ready-to-display markup instead of raw (JSON) data.

The PartialView Method

In ASP.NET MVC, a partial view is analogous to user controls in ASP.NET Web Forms. A partial view is a chunk of HTML that can be safely inserted into an existing DOM. Most commonly, partial views are used to componentize Razor views and make them easier to build and update. Partial views can also be returned directly from controller methods. In this case, the browser still receives text/html content but not necessarily HTML content that makes up an entire page. As a result, if a URL that returns a partial view is directly invoked from the address bar of a browser, an incomplete page may be displayed. This may be something like a page that misses title, script and style sheets. However, when the same URL is invoked via script, and the response is used to insert HTML within the existing DOM, then the net effect for the end user may be much better and nicer.

With an eye on Figure 1, let’s see what we could do to have users click a delete button and update the list of customers without spinning a full page refresh.

The most important point to remember is that an endpoint that returns a partial HTML view should only be called via script. The list of customers of Figure 1, therefore, might be generated from code as below. By the way, the sample code below is based on Bootstrap.

Clicking the button triggers a JavaScript function. The JavaScript function receives the ID of the customer the user intends to delete and calls the appropriate controller method. When the remote call returns the script grabs the HTML chunk and updates the DIV element container of the customer list.

On the server side, the Delete method on the controller class becomes as shown below:

To make the actual behavior of the method clear to occasional readers of the code from the beginning, you can also change the return type of the controller method from a generic ActionResult to a more specific PartialViewResult type. The actual behavior doesn’t change; but the code is inherently easier to understand.

Let’s review the new mechanics of the page. When users click to delete a record, a POST request is made to a HTTP endpoint within a controller. The controller deletes the record and returns a chunk of HTML for the script to use and update the current DOM. The request is sent over as a POST, but at the end of the day there’s no strict requirement for a POST verb; a GET would work anyway. At the same time, because the request is expected to cause a state change on the server side, it’s ideally configured to be a POST. The request is placed using JavaScript and it’s not tracked by the browser. Whether it goes as a GET or POST, it won’t be repeatable if the user hits F5. For the end user, the net effect is that changes become immediately visible without any page flickering.

Limitations of this Technique

The amount of script code used for this technique is the bare minimum. This is good and bad news at the same time. It’s good because any line of code is under your strict control: it’s all about jQuery, the DOM and you. It’s bad because dependencies between controller, Razor view and JavaScript code are explicit. If you happen to change, say, the ID of the DIV in the Razor view you may break the script. Likewise, if you modify the script or the code in the controller you may cause unexpected results in the HTML being served to the user. In a nutshell, the three elements (controller, Razor view, script code) must work together in perfect harmony.

There’s another drawback, however, in the technique discussed so far. To see what it is, have a look at Figure 2.

2118-clip_image004.jpg

The drop-down menu in the figure has one item for each customer in the list. This means that when the user clicks a button to delete a customer from the client-side, it is not enough to refresh the list. Also the contents of the drop-down should be updated. This is not an issue, instead, if the entire page is rebuilt on the server as during a classic postback and a full page refresh. Likewise, this is not an issue if you are in the context of SPA. In this case, the request brings back raw JSON data that the client-side logic knows how to handle to update all the involved segments of the user interface.

More in general, whenever you send a request via JavaScript multiple segments of the user interface may be touched by the effects of that request. And when the request is coded to return ready-to-display markup, multiple chunks of HTML should be returned. Let’s see how to do this.

Combining Multiple Partial Views Together

The idea therefore is that a controller method (say, Delete) is invoked via JavaScript and returns multiple partial views. By design, a controller method must return a type derived from ActionResult but none of the predefined action result types supports multiple HTML chunks. Hence, a new action result type is in order.

The constructor of the MultipleViewResult type accepts a list of PartialViewResult types and caches them internally in a read-only collection. An action result type must necessarily override the ExecuteResult method declared as abstract in the base class. ASP.NET MVC requires that an implementation of ExecuteResult just writes down any response to the output stream.

Because MultipleViewResult contains multiple instances of PartialViewResult, it is sufficient to loop over the collection of PartialViewResult objects and ask each to execute. Finally, a separator must be added so that on the client side the various chunks can be easily detected. In principle, you can use any character (or combination thereof) to separate HTML chunks. In practice, instead, any character, or combination of characters, that is common in the HTML body can break the script. I usually use a string like “—|||—“, but feel free to adapt it to your own purposes. With the MultipleViewResult class in place, here’s how to rewrite the controller method:

In the example, the two partial views use the same view model. This is not a limitation though. The script code in the client page receives a single string and unpacks it before display.

Look at Figure 2 again and imagine a user that clicks on the selected customer. Figure 3 shows the final effect. The customer 435 has been removed from the backend database and the view has been updated instantaneously and without full page refresh. For the user, it is definitely a great experience.

2118-clip_image006.jpg

Summary

Pages that are quick to respond to users are essential these days. This requirement is behind the huge success of single-page applications and frameworks such as AngularJS. Building a SPA, though, may be hard not so much for the technologies involved, but because of the skills required to build SPAs effectively. The approach presented in this article represents a quick-and-easy way to extend existing ASP.NET MVC applications and make them as responsive as SPAs without learning a brand new framework.

In terms of performance it’s undeniable that downloading raw JSON is faster than downloading HTML chunks. However, plain download of HTML chunks is fast enough compared to full page refresh for the vast majority of users.

How you log in to Simple Talk has changed

We now use Redgate ID (RGID). If you already have an RGID, we’ll try to match it to your account. If not, we’ll create one for you and connect it.

This won’t sign you up to anything or add you to any mailing lists. You can see our full privacy policy here.

Continue

Simple Talk now uses Redgate ID

If you already have a Redgate ID (RGID), sign in using your existing RGID credentials. If not, you can create one on the next screen.

This won’t sign you up to anything or add you to any mailing lists. You can see our full privacy policy here.

Continue