views:

2251

answers:

4

Say I have a page that display search results. I search for stackoverflow and it returns 5000 results, 10 per page. Now I find myself doing this when building links on that page:

<%=Html.ActionLink("Page 1", "Search", new { query=ViewData["query"], page etc..%>
<%=Html.ActionLink("Page 2", "Search", new { query=ViewData["query"], page etc..%>
<%=Html.ActionLink("Page 3", "Search", new { query=ViewData["query"], page etc..%>
<%=Html.ActionLink("Next", "Search", new { query=ViewData["query"], page etc..%>

I dont like this, I have to build my links with careful consideration to what was posted previously etc..

What I'd like to do is

<%=Html.BuildActionLinkUsingCurrentActionPostData
        ("Next", "Search", new { Page = 1});

where the anonymous dictionary overrides anything currently set by previous action.

Essentially I care about what the previous action parameters were, because I want to reuse, it sounds simple, but start adding sort and loads of advance search options and it starts getting messy.

Im probably missing something obvious

+1  A: 

Whenever you find yourself writing redundant code in your Views, write a helper. The helper could explicitly copy the parameters, as you're doing it now, or it could iterate the entire collection and copy automatically. If it were me, I would probably choose the former. Then you can just call your new helper, instead of rebuilding the parameters every time you make a link.

Craig Stuntz
+1  A: 

I'm a little iffy as to what you are actually trying to do here. I think you are trying to automate the process of creating a list of links with only small changes between them. Apparently in your case the id number of "Page".

One way to do it, although possibly not the best is like so (My code makes use of a basic and contrived Product list and the ViewPage and PartialViewPage both use Strongly Typed Models):

On your ViewPage you would add code like this:

<div id="product_list">
        <% foreach (TestMVC.Product product in ViewData.Model)
           { %>
            <% Html.RenderPartial("ProductEntry", product); %>
        <% } %>
</div>

Your Partial View, in my case "ProductEntry", would then look like this:

<div class="product">
    <div class="product-name">
        <%= Html.ActionLink(ViewData.Model.ProductName, "Detail", new { id = ViewData.Model.id })%>
    </div> 
    <div class="product-desc">
        <%= ViewData.Model.ProductDescription %>
    </div>       
</div>

All I'm doing in that Partial View is consuming the model/viewdata that was passed from the parent view by the call to Html.RenderPartial

In your parent view you could modify a parameter on your model object before the call to Html.RenderPartial in order to set the specific value you are interested in.

Hope this helps.

slude
+6  A: 

I had a similar problem inside an HtmlHelper; I wanted to generate links that linked backed to the current page, with a small adjustment in parameters (think incrementing the page number). So if I had URL /Item/?sort=Name&page=0, I wanted to be able to create links to the same page, but just change the page parameter, and have the sort parameter automatically included (ie /Item/?sort=Name&page=1).

My solution was this (for use in an HtmlHelper extension method, but since you can access the same data almost anywhere in MVC, you can adapt it easily to your uses):

private static RouteValueDictionary CreateRouteToCurrentPage(HtmlHelper html)
{
    RouteValueDictionary routeValues 
         = new RouteValueDictionary(html.ViewContext.RouteData.Values);

    NameValueCollection queryString 
         = html.ViewContext.HttpContext.Request.QueryString;

    foreach (string key in queryString.Cast<string>())
    {
        routeValues[key] = queryString[key];
    }

    return routeValues;
}

What the method does is take the RouteValueDictionary for the current request and create a copy of it. Then it adds each of the query parameters found in the query string to this route. It does this because, for some reason, the current request's RouteValueDictionary does not contain them (you'd think it would, but it doesn't).

You can then take the resultant dictionary, modify only a part of it, for example:

routeValues["page"] = 2;

and then give that dictionary to the out-of-the-box HtmlHelper methods for them to generate you a URL/etc.

Daniel Chambers
the only real solution to the question, should've been accepted.
daniel
Although I can't see any other way of doing this, all that collection copying doesn't look very pretty.
Martin Brown
+1  A: 

Following helper method does just that :

    public static string EnhancedActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, bool keepQueryStrings)
    {
        ViewContext context = helper.ViewContext;
        IDictionary<string, object> htmlAttributes = null;
        RouteValueDictionary routeValues = null;
        string actionLink = string.Empty;
        if (keepQueryStrings && context.RequestContext.HttpContext.Request.QueryString.Keys.Count > 0)
        {
            routeValues = new RouteValueDictionary(context.RouteData.Values);
            foreach (string key in context.RequestContext.HttpContext.Request.QueryString.Keys)
            {
                routeValues[key] = context.RequestContext.HttpContext.Request.QueryString[key];
            }
        }            
        actionLink = helper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);
        return actionLink;
    }
jalchr