Question

I have an A - Z directory 'widget' that I have on every page. If the user is on the home page and they click something in the directory, I want to load up the directory page with the corresponding result loaded. But if the user is on the directory page and they click something, I want to asynchronously load the result without doing a page refresh.

The directory widget has links that point to the DirectoryResult action method on the GroupController, which would normally return a PartialView if they're on the directory page. But if they're not on the directory page, I redirect to the main Directory action method which returns a View and loads the entire page.

This is the code in question:

    public ActionResult DirectoryResult(string search)
    {
        if (Request.IsAjaxRequest())
        {
            var groups = _groupService.GetGroupsBySearchExpression(search);
            var premiumGroups = _groupService.FilterPremiumGroups(groups);

            return PartialView(new FundDirectoryViewModel
            {
                Groups = groups,
                PremiumGroups = premiumGroups
            });
        }
        else
        {
            TempData[UIMessageDataKeys.FundDirectorySearch] = search;
            return RedirectToAction("Directory", "Group");
        }
    }

I showed this to one of the guys in the office and his immediate response was "that's a hack!". I don't know whether to agree with him or not though, because I don't know any better way to do it.

For reference, this is the definition of the widget that exists on every page:

<div id="DirectoryList" class="directory-list">
    <span>Fund Directory</span>

    <% var letters = new [] { "A", "B", "C", "D", "E", "F", "G", "H", "I", ... }; %>
    <% var current = (Model.Search.IsNotNullOrEmpty()) ? Model.Search : "A"; %>
    <% foreach (var letter in letters) { %>

        <span>
            // use HtmlHelper extension to generate links as our system needs them
            <%= Html.RouteActionLink("funddirectory", "DirectoryResult"
                , letter
                , (letter.ToLower() == current) ? new { @class = "active" } : new { @class = "" })%>

        </span>

    <%} %>
</div>

Is there a better way for me to determine whether I should return a PartialView or a View depending on the page the request comes from?

Was it helpful?

Solution

While your view can definitely be improved to avoid all this spaghetti code (using editor/display templates and HTML helpers and avoid hard-coding an alphabet in a view :-)), the action method seems fine to me. Using Request.IsAjaxRequest to determine whether the action has been requested with AJAX and return a partial view or if not redirect is perfectly fine.

What could be considered as a hack is the usage of TempData instead of using a query string because if the user presses F5 on the redirected page he will loose the context, but if this is the behavior you expect then it's ok.

While I am not familiar with the context I would be interested in the arguments that the guys at your office used to support their reaction of this being a hack.

OTHER TIPS

While Darin is 100% correct and your code is not a hack I usually prefer making two Actions with different names and signatures. This is especially easy if you use an AjaxOnly action filter such as: http://helios.ca/2009/05/27/aspnet-mvc-action-filter-ajax-only-attribute/

public ActionResult DirectoryResult(string search)
{        
        var groups = _groupService.GetGroupsBySearchExpression(search);
        var premiumGroups = _groupService.FilterPremiumGroups(groups);

        return PartialView(new FundDirectoryViewModel
        {
            Groups = groups,
            PremiumGroups = premiumGroups
        });        
}

//optional [AjaxOnly]
public ActionResult DirectoryAjaxResult( string search )
{
        TempData[UIMessageDataKeys.FundDirectorySearch] = search;
        return RedirectToAction("Directory", "Group");
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top