A single page application I created where contents are in server side and with navigation from one link to other link the content are displaying in same page. As there have no redirect to any other page so Ajax loading is more applicable in such single page application. By clicking on any of the navigation link the detail content will only be requested from server. So full page is not render in such case only the detail portion of page. In such scenario Partial View of ASP.NET MVC is suitable. The detail content will be displayed as Partial View and with Ajax call we will return only Partial View. Ajax output will be only detail content and will be displayed to user setting as innerHTML or append inside a <div>. I used ASP.NET MVC 4 to create that application. The solution will also applicable for ASP.NET MVC 2.
From the Url of the following page you can understand Home is the Controller. DynamicContent is Action and Page1 is route parameter Id. As a single page application if I click on the navigation like in right side, only the detail content will changed. If I click on Page 2 link then the content will be “Content will be display here Page2”. Here the links are not changing and only detail content is changing with Ajax call.
Let create a Partial View DynamicContentPartialView.cshtml for detail content like this code:
<div> Content will be display here @ViewBag.SelectedPage </div>
Now inside DynamicContent.cshtml partial view is rendered with RenderParial call.
<article> <div id="dynamicContent"> @{ Html.RenderPartial("DynamicContentPartialView"); } </div> </article> <aside> <ul id="dynamicActionLinks"> <li>@Html.ActionLink("Page 1","DynamicContentPartialView","Home",new{id="Page1"},null)</li> <li>@Html.ActionLink("Page 2", "DynamicContentPartialView", "Home", new { id = "Page2" }, null)</li> <li>@Html.ActionLink("Page 3", "DynamicContentPartialView", "Home", new { id = "Page3" }, null)</li> </ul> </aside>
Here in the code block DynamicContentPartialView control is displayed using RenderPartial call. And there have navigation link for Ajax call. So when first time initial loading or you refresh the page without Ajax call RenderPartial() method will be called. which will display content according the Url route value Id.
The server side code of DynamicContent Action is very simple.
public ActionResult DynamicContent(string id) { ViewBag.SelectedPage = id; return View(); }
For making it more simple the detail content is displaying only the selected route value Id. So that we can only concentrate on main factors.
So if the Url will be like: http://localhost:1037/Home/DynamicContent/Page1 . Here Page1 is the Id route value then the output will be same as above page image.
Now in case of clicking one of right side links we will make Ajax call and we will fetch the detail content. Let say Page 2 link is clicked from right side navigation link. The action link for Page 2 is
@Html.ActionLink("Page 2", "DynamicContentPartialView", "Home", new { id = "Page2" }, null)
Here if you see the html hyperlink value after rendering ActionLink it will look like:
<a href="/Home/DynamicContentPartialView/Page2">Page 1</a>
Now from href attribute you can see Home is controller and DynamicContentPartialView is action. But we will not make redirect to DynamicContentPartialView. we will call DynamicContentPartialView with ajax call.
Javascript code for handling click on the navigation link is:
$("#dynamicActionLinks li a").click(function () { var href = $(this).attr("href"); LoadAjaxContent(href); return false; }); function LoadAjaxContent(href) { $.ajax( { url: href, success: function (data) { $("#dynamicContent").html(data); } }); }
Click event of <a> link is handled here in above code block. An Ajax request is made with href value of <a> (<a href="/Home/DynamicContentPartialView/Page2">Page 1</a>). So when $.ajax(fn); is called then this request goes to DynamicContentPartialView Action inside Home Controller. The code block for DynamicContentPartialView is:
public ActionResult DynamicContentPartialView(string id) { ViewBag.SelectedPage = id; return PartialView("DynamicContentPartialView"); }
It also takes route value Id same as DynamicContent Action but here it return DynamicContentPartialView. where DynamicContent Action is returning full View. PartialView call basically write the content inside DynamicContentPartialView to the response. We will get only the content by making ajax call.
<div> Content will be display here Page2 </div>
and inside success method of $.ajax() call we set the InnerHTML of <div id=”dynamicContent”></div>. Hence loading contents with ASP.NET MVC partial view is very easy with PartialView.
As ajax call is not redirecting to another page so now problem in case of maintaining history of browser with Url. When you will click on Page 2 navigation link Url of browser will not changed as it is a ajax request. Browser will display Url as http://localhost:1037/Home/DynamicContent/Page1 where it should be http://localhost:1037/Home/DynamicContent/Page2 but it is displaying detail content of Page2. So when we will make Ajax request we will also have to change Url of broswer. So that user can bookmark http://localhost:1037/Home/DynamicContent/Page2 also.
HTML5 browser supports history.js api which can not be applicable for non HTML5 browser. Let see the pushState() code while making ajax request.
$("#dynamicActionLinks li a").click(function () { var href = $(this).attr("href"); if (window.history && window.history.pushState) { LoadAjaxContent(href); var displayUrl = href.replace("PartialView", ""); window.history.pushState(href, "Page Title", displayUrl); } else { $.address.value(href); } return false; });
Here displayUrl is used for displaying Url to the browser. Though we are making ajax request to DynamicContentPartialView Action but the browser will display http://localhost:1037/Home/DynamicContent/Page2. Now if you click on back and forward to http://localhost:1037/Home/DynamicContent/Page2. onpopstate event will raise. Let see the code :
window.onpopstate = function (event) { if (event.state != null) LoadAjaxContent(event.state); };
Here the state object contains http://localhost:1037/Home/DynamicContentPartialView/Page2 as state ( first argument of pushState() method). And here we again make ajax request. You could also have detail content of Page2 inside pushState. Here I made ajax call again to fetch detail content from PartialView. But not all browser ( IE6 to IE9) does not support history.js api. So we need to check for those browser.
For non- HTML5 browser, only option for maintaining history is changing window.location.hash. I found a jquery nice plugin Address.js. I have also used it for ajax call
$.address.value(href);
It also fire an event as like:
$.address.change(function (event) { if (event.value == "/") { if (window.location.href.indexOf("DynamicContent") != -1) { var quarylocation = window.location.href; LoadAjaxContent(quarylocation.replace("DynamicContent", "DynamicContentPartialView")); } return; } try { LoadAjaxContent(event.value); } catch (e) { } });
We can get the value of hash with event.value. As we did not push the state of browser when Full Page load with called with DynamicContent action . So inside the DynamicContent view the first state is saved as:
<div id="dynamicContent"> <script type="text/javascript"> if (window.history && window.history.pushState) { var href = window.location.href; var backUrl = href.replace("DynamicContent", "DynamicContentPartialView"); window.history.pushState(backUrl, "Page Title", href); } else { $.address.value(window.location); } </script> @{ Html.RenderPartial("DynamicContentPartialView"); } </div>
With this call it also have the state of first load without ajax call. So when we will back to initial state it from browser history it will also make ajax request for initial state.
Here I have tried to give your same features as they get full page load but as modern web application where history is maintained with ajax call and only the required detail content is requested from server.
My colleague Masudur Rahman also worked with me in pair programming.
Here you can download the solution with above explained code https://dl.dropbox.com/u/20275838/historybookmark.rar