Question

I have been searching extensively on how to create a navigation link that will display an ajax Loading icon while the action method is running in MVC 3 using the Razor view engine. While I have found plenty of information regarding adding a loading icon, all of them seem to pertain to posting data within the current view.

I have a simple menu page that has actionlinks to call action methods on other pages, which in turn will render a view. An example to go to the OrdersController and Index action method is displayed below.

Menu.cshtml

@Html.ActionLink("Orders", "Index", "Orders",null,new { id = "OrderLink" })<br />

I am trying to modify the link above in a way that while the Index action is processing and before the view is returned, to show an ajax loading icon.

I have created a div that contains the loading icon and placed it in my shared _Layout.cshtml page.

_Layout.cshtml

<div id="ajaxLoaderDiv" style="position:fixed; top:40%; left:45%; z-index:1234; display:none;">
    <img src="@Url.Content("~/Images/AjaxLoaderImg.gif")" alt="Loading..."class="ajax-loader" />
</div>

To try and display the ajax loading icon I tried changing my link to an Ajax ActionLink.

Menu.cshtml

@Ajax.ActionLink("Orders", "Index", "Orders", new AjaxOptions { LoadingElementId="ajaxLoaderDiv" });<br />

This works in the sense that it displays the ajax Loading icon for a few seconds. And I can even put a breakpoint inside the Index action method of the OrdersController and it will hit it. But when the Action Method completes, the Orders\Index.cshtml never renders to the page. I just remain on the Menu\Index.cshtml page.

I know that the Orders/Index.cshtml is supposed to be rendering a view.

OrdersController.cs

public ActionResult Index()
{
    ....

return View(OrdersViewModel);
}

What am I doing wrong here? Is this view being returned to my ajax call rather than rendering? How can I get this functionality?

If I am going about this in the wrong manner, let me know.

Should I be doing this inside the target page's action method instead of on the link when making the request? Maybe by displaying the loading icon as the first line of code in the Index action method that I am requesting, and hiding it again just before returning the view?

I would maybe try and do this through through a JQuery Show/hide combination?

<script type="text/javascript">
    function showLoadingSpinner() {
        $("#ajaxLoaderDiv").show();
    };
    function hideLoadingSpinner() {
        $("#ajaxLoaderDiv").hide();
    };

</script>

Thanks in advance for and and all help and advice

Chad

Was it helpful?

Solution

My Resolution:

I resolved my issue on my own, but thought I would post the resolution for any others that are attempting something similar.

I kept the link on my menu.cshtml page as an @Html.ActionLink rather than an ajax link. But I added an onclick html attribute that called the javacript to show the spinner.

menu.cshtml

@{
    ViewBag.Title = "Menu";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container" style="text-align:center">
    <div>
        @Html.ActionLink("Orders", "Index", "Orders", null, new { id = "OrderLink", onclick="showPageLoadingSpinner()" })
    </div>
</div>

I have the scipt for the spinner in my _Layout.cshtml partial view. which is referenced in my menu.cshtml view: Layout = "~/Views/Shared/_Layout.cshtml";

Below is my full _Layout.cshtml page including the ajax div and script references.

_Layout.cshtml

<html>
<head>
    @Content.Script("jquery-1.5.1.min.js", Url)
    @Content.Script("jquery.unobtrusive-ajax.min.js", Url)
</head>

<body>
    <div id="ajaxLoaderDiv" style="position:fixed; top:40%; left:45%; z-index:1234; display:none;">
    </div>
    <div class="container">
         @RenderBody()
    </div>
</body>
</html>

<script type="text/javascript">
function showPageLoadingSpinner() {
     $('#ajaxLoaderDiv').show();
     $('#ajaxLoaderDiv').append(
                '<img src="@Url.Content("~/Images/AjaxLoaderImg.gif")" alt="Loading..."class="ajax-loader" />'
     );
 }

 function hidePageLoadingSpinner() {
     setTimeout(function () {
         $('.ajax-loader').remove();
         $('#ajaxLoaderDiv').hide();
     }, 20000);
 }

</script>

This works for me because on the menu.cshtml page when you click the link the javascript will run display the spinner before the actionlink sends you to the next page. Therefore you will see the spinner while the page you are navigating to is loaded.

When the new page is ready, it will render, and display. While the new page that you are sent to (Orders/Index.cshtml) uses the same layout page which contains the ajax div, it loads the new page with the ajaxloader div property display:none set by the layout. This ends up removing the ajax spinner that was displayed while loading the page.

Therefore there is no need to ever call the javascript to hide the spinner because it is always removed when the new page renders.

Unless someone else tells me that this is an inappropriate approach and successfully show me a better way, I am going to accept this as the answer.

OTHER TIPS

Change your action method signature to something like this:

public PartialViewResult Index()
{
    ....

return PartialView(OrdersViewModel);
}

Also, for the Ajax.ActionLink to work properly, you need to add a reference to unobtrusive ajax:

Obtainable via nuget unless it's not in your scripts folder.

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top