Reputation:
I need to create a link that will be based on my search criteria. For example:
localhost/Search?page=2&Location.PostCode=XX&Location.Country=UK&IsEnabled=true
Parameters in this link are the values of properties in my SearchViewModel.
Ideally I'd like to have something on the lines of:
@Html.ActionLink("Search","User", Model.SearchCriteria)
Is this supported by default or do I need to pass properties of my view model into RouteValueDictionary type object and then use that?
My goal is to write a paging helper which would generate page numbers and append the search criteria parameters to the generated links.
E.g.
@Html.GeneratePageLinks(Model.PagingInfo, x => Url.Action("Index"), Model.SearchCriteria)
I've combined your solutions with suggestion from PRO ASP.NET MVC 3 book and ended up with the following:
Helper for generating links. Interesting part is pageUrlDelegate parameter which is later used to invoke Url.Action for generating links:
public static MvcHtmlString PageLinks(this HtmlHelper html, PagingInfoViewModel pagingInfo,
Func<int,String> pageUrlDelegate)
{
StringBuilder result = new StringBuilder();
for (int i = 1; i <= 5; i++)
{
TagBuilder tagBuilder = new TagBuilder("a");
tagBuilder.MergeAttribute("href", pageUrlDelegate(i));
tagBuilder.InnerHtml = i.ToString();
result.Append(tagBuilder.ToString());
}
return MvcHtmlString.Create(result.ToString());
}
Then in the view model:
@Html.PageLinks(Model.PagingInfo, x => Url.Action("Index","Search", new RouteValueDictionary()
{
{ "Page", x },
{ "Criteria.Location.PostCode", Model.Criteria.Location.PostCode },
{ "Criteria.Location.Town", Model.Criteria.Location.Town},
{ "Criteria.Location.County", Model.Criteria.Location.County}
}))
)
I'm still not happy with property names in Strings, but It'll have to do for now.
Thank you :)
Upvotes: 12
Views: 14045
Reputation: 1038770
Ideally I'd like to have something on the lines of:
@Html.ActionLink("Search","User", Model.SearchCriteria)
Unfortunately that's not possible. You will have to pass properties one by one. You could indeed use the overload which takes a RouteValueDictionary
:
@Html.ActionLink(
"Search",
"User",
new RouteValueDictionary(new Dictionary<string, object>
{
{ "Location.PostCode", Model.SearchCriteria.PostCode },
{ "Location.Country", Model.SearchCriteria.Country },
{ "IsEnabled", Model.IsEnabled },
})
)
Of course it's probably better to write a custom ActionLink helper to do this:
public static class HtmlExtensions
{
public static IHtmlString GeneratePageLink(this HtmlHelper<MyViewModel> htmlHelper, string linkText, string action)
{
var model = htmlHelper.ViewData.Model;
var values = new RouteValueDictionary(new Dictionary<string, object>
{
{ "Location.PostCode", model.SearchCriteria.PostCode },
{ "Location.Country", model.SearchCriteria.Country },
{ "IsEnabled", model.IsEnabled },
});
return htmlHelper.ActionLink(linkText, action, values);
}
}
and then:
@Html.GeneratePageLink("some page link text", "index")
Another possibility is to pass only the id
and have the controller action fetch the corresponding model and values from wherever you fetched it initially in the controller action that rendered this view.
Upvotes: 11
Reputation: 24236
Here's some code taken from Steve Sanderson's Tekpub samples that is broadly what you're looking for. First define an extension method on your object -
public static RouteValueDictionary DetailsRouteValues(this JobAd jobAd)
{
return new RouteValueDictionary
{
{ "controller", "Jobs" },
{ "action", "Details" },
{ "id", jobAd.JobAdId },
{ "country", MakeSimpleUrlSegment(jobAd.LocationCountry) },
{ "city", MakeSimpleUrlSegment(jobAd.LocationCity) },
{ "title", MakeSimpleUrlSegment(jobAd.Title) }
};
}
Then call Html.RouteLink
using that method -
<%= Html.RouteLink(ad.Title, ad.DetailsRouteValues())%>
Upvotes: 1