views:

58

answers:

2

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?

+2  A: 

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.

Darin Dimitrov
+1 for formatting `F5` as a key
SimpleCoder
@SimpleCoder, that's the funniest reason for a +1 I have ever got :-)
Darin Dimitrov
@Darin Dimitrov, :) I'm glad. I have just never seen that tag used on here before.
SimpleCoder
I think the reasoning in support of his opinion that it was a hack is that I wasn't calling the Directory method directly. I took your advice on the use of TempData, and changed the code to use`return RedirectToAction("Directory", "Group", new RouteValueDictionary{{"search", search}});`. Interestingly though, I used TempData because that's what was suggested in Sanderson's Pro ASP.NET MVC book. He was saying that TempData is valid when doing redirects, because the value is only stored until the next request is complete. I'll look into your suggestion on improving the view. Thanks
DaveDev
TempData is good for example when you want to store some message that you show to the user after a redirect, but not good for persisting models.
Darin Dimitrov
+1  A: 

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");
}
jfar