Introduction
Sometimes, an index page may take too long to load, usually because the needed data comes from long running process or slow resources (slow DB, lazy web services, particular data calculation, etc.).
Of course, the first thing to do is to try to clear these problems by means of DB optimization, code review, data paging and so on. But as a last chance, to improve the experience of a user waiting in front of a browser, what we can do is to provide him something as soon as we have it, and to complete page later, as soon as data are available.
Partial Views and HtmlHelper Extension Methods
Everybody knows and uses partial views in MVC and how they are included in a main View
, typically an index page.
This extension is a new HtmlHelper
doing the dirty job of displaying a 'preview' partial view, calling the real one behind the scenes and finally showing the complete version as soon as ready, possibly calling it with one line of code.
Using the Code
@Html.AsyncPartial("MyController","MyAction")
This is the simplest usage of the extension that, in place of Html.Partial
, will provide to:
- load directly in the parent view a partial named in this case "
MyAction_Preview
", that typically is a Model-less partial view. It should appear similar to the final view, but just presenting a waiting spinner or what you prefer.
- wrap the HTML of the preview partial into a container, and inject, as inline code or wherever we decide, the JavaScript code that will call
MyController/Myaction
- replace the container content with the async response got by JavaScript
The result is the preview Partial view ...
...replaced by the complete Partial view once data is available.
The complete extension signature is as follows:
public static MvcHtmlString AsyncPartial(this HtmlHelper helper,
string controller,
string action,
string previewPartialName=null,
AjaxMethods method = AjaxMethods.GET,
TempDataDictionary TempData=null)
previewPartialName
: if provided, it will be the name of the 'preview' partial view displayed while waiting for the async call to be completed. Defaults to "{action}_preview
"
method
: POST
or GET
to be used to call the Action
in the controller. Defaults to GET
.
TempData
: If provided, the Dictionary
will contain the JS script to be added to the page calling the partial view. If not, the script will be included as inline code right after the partial view HTML as to be executed during the rendering of the HTML page.
In the main View
, this is what you need:
@using Helper.Extensions
......
@Html.AsyncPartial("home", "getdata")
The @using
directive imports the namespace
used in the helper class (downloadable).
Of course, in this case, you would need a home
controller containing a getdata
Action.
public ActionResult GetData()
{
PopulationModel model = new PopulationModel();
return View(model);
}
Views getdata.cshtml
and getdata_preview.cshtml
should exist. The first one in this case needs a model, the second doesn't.
Script Position
If you choose to pass TempData
dictionary to the helper, the JS script will be added to the main view where you type:
<script>@Html.Raw(TempData["script"])</script>
By default, JS script is injected as inline code after partial view HTML.
Click to Refresh
The white refresh-icon on the top right of the partial view will actually refresh the content sending an Ajax request. If you need, just include in your view an anchor tag having a data-partial-refresh
attribute. An onclick
event will be added by the helper, calling the needed JS function.
<a data-partial-refresh href="#">
<span class="glyphicon glyphicon-refresh"></span></a>
Author
Max Aronica is a senior .NET full-stack Developer working as external consultant for enterprise customers in Rome, Italy.
History
- 18/09/2017 : attached missing Demo solution