Passing page for routelink - asp.net-mvc

I have a problem with getting my pager-function to work. For some reason it doesnt like when I try to pass the current pageindex +1 to the same page for it to display next one.
<% if (Model.Users.HasNextPage) { %>
<%= Html.RouteLink(">>>", "Users", new { page = (Model.Users.PageIndex +1) })%>
<% } %>
If I only use: ”>>>”, ”Users) it works, although the next page function doesn’t work since it doesn’t assign next value.
If I debug Model.Users.PageIndex it has the value 0 when it loads the page (which it should have).
Somehow it doesn’t like the “new”-thingy at the end
I have swedish errors on, but it complains something about not finding the path/route/reference to the location of User, or how its set.
The actionresult looks like:
public ActionResult Users(int? page){
const int pagesize = 10;
var pagnatedUsers = new PaginatedList<User>(_us.GetUsers(), page ?? 0, pagesize);
return View("Users", new UserAdminEditViewModel { Users = pagnatedUsers });
}
Thanks in advance
/M

I'm going to guess "Users" in your second parameter to Html.RouteLink is supposed to refer to your controller action name. RouteLink actually doesn't have an overload of (string linkText, string actionName, object routeValues) which is what it appears you are trying to provide.
The overload you are calling is actually asking for the routeName in the second parameter, and you don't have such a route defined!
Try this
Html.RouteLink(">>>", new { controller="Home", action="Users", page = (Model.Users.PageIndex +1) })%>
substituting for your actual controller name.
Update/response: I was trying to explain why your code wasn't working as expected. Indeed, if you use ActionLink instead with your original parameters that is also a solution - and probably the better one since it seems to be what you want.
RouteLink and ActionLink are essentially the same under the covers (they both end up calling the same underlying code that actually generates the link). The difference is only in context of use - RouteLink is there to help you generate links based on your routing configuration (eg. by a route name) and ActionLink is there for links based on your controller actions (eg. by an action name). And there is plenty of overlap where you could use both of them in the exact same way.

I got that RouteLink code from the Nerddinner-example. And now when I changed to ActionLink instead of RouteLink it worked.
Not quite sure what the difference is between having ActionLink or the way Kurt describes.

Related

T4MVC OptionalParameter values implied from current context

I've noticed what I believe to be some odd behavior with T4MVC. Specifically, I'm attempting to build an ActionLink (using the HtmlHelper) for an action where the optional parameter value is null. This works fine most of the time. However, if the current route is of the same for which the ActionLink is being built AND the OptionalParameter has a non-null value, the resulting ActionLink will specify the value of the optional parameter from the current route context.
That's a wordy explanation, I think code will help clarify.
Controller
public virtual ActionResult Today(int? lineNumber = null)
{
return Index(DateTime.Today, DateTime.Today, lineNumber);
}
Route
context.MapRoute(
"TodaysProductionSchedules",
"Production/{Controller}/Today/{lineNumber}",
new
{
area = AreaName,
controller = MVC.Production.ProductionSchedules.Name,
action = MVC.Production.ProductionSchedules.ActionNames.Today,
lineNumber = UrlParameter.Optional
});
Razor
#Html.ActionLink("Show Today", MVC.Production.ProductionSchedules.Today(null))
As I mentioned earlier, if I am not currently on a view which is mapped to this route, the link will be generated correctly. However, if the current view does map the this route AND I either omit the value or supply null (as seen in the razor snippet), the lineNumber parameter will take its value from the current route value.
I think this might be a bug in T4MVC so I'll post a link to this topic on the T4MVC codeplex site as well. Thanks in advance!
Update 7/30/2012: This is fixed in T4MVC 2.10.1!
This was actually a recent regression from the model unbinder change. In t4mvc.tt around line 639, can you try changing AddRouteValues to the following:
public static void AddRouteValues(RouteValueDictionary routeValueDictionary, string routeName, object routeValue) {
IModelUnbinder unbinder;
if (routeValue == null)
{
unbinder = DefaultModelUnbinder;
}
else
{
unbinder = ModelUnbinders.FindUnbinderFor(routeValue.GetType()) ?? DefaultModelUnbinder;
}
unbinder.UnbindModel(routeValueDictionary, routeName, routeValue);
}
Original answer:
I think generally in MVC, in many scenarios when a value is omitted from the new route, it gets its value from the current route, assuming that the high level values are the same (hence the two different cases you see).
So now the question is whether T4MVC can/should do something to avoid this behavior. I haven't checked the exact logic, but maybe if it always set this value in the route, that would disable this unwanted behavior.
But I think the first step is to fully understand the MVC behavior that's at play here before tackling the T4MVC case.
Feel free to take the investigation further and send a PR with the fix! :)

Html.DisplayFor and Html.EditorFor displays different values

I'm completely stumped.
I'm doing a searchform in MVC2 (I've done dozen others on this project, all working fine.)
Global.asax has this route:
routes.MapRoute("OnlineHelpSearchIndex",
"Help/Search/{expression}/{page}",
new { controller = "OnlineHelp", action = "Search", expression = UrlParameter.Optional, page=1 });
The expression is a base64 encoded string. I decode it in controller, pass it to a model which has a property named Expression, and display it in a PartialView in a TextBox. (Then when the user clicks a link or presses enter, I encode the string in javascript and send it to "/Help/Search/"+value)
I have several searchboxes built this way (each with a route SomeModule/Search/{expression}), and one of them is not working.
<%:Html.DisplayFor(m => m.Expression)%>
<%: Model.Expression %>
<%:Html.TextAreaFor(m => m.Expression)%>
<%:Html.TextBoxFor(m => m.Expression)%>
<%:Html.EditorFor(m => m.Expression)%>
The first two display the correct expression, the other three displays the expression in the url.
I tried hardcoding a string into the model, the first two displayed the hardcoded string, the other three displayed whatever was in the url. How is it possible?
(I even tried with JS disabled, so it is a server side issue)
I know this is an old thread, but I figured I would answer it anyway. The reason this is happening is intentional, it is due to ModelState. See this question for another case:
Asp.net MVC ModelState.Clear
Long story short, you're POSTing form data to a controller and returning a View, and using Helpers. Therefore, MVC assumes this is a failure on validation and is returning the ModelState value, not the value of your Model data. The first two are displaying correctly because they are not editors, the other 3 are editors, so they're showing ModelState.
Either call ModelState.Clear() in the controller to blow it away, or implement another design pattern, such as POST, Redirect, GET.
Try to change the name of the expression parameter in both the routes.MapRoute and in your OnlineHelp/Search controller/action method:
routes.MapRoute("OnlineHelpSearchIndex",
"Help/Search/{exprs}/{page}",
new { controller = "OnlineHelp", action = "Search", exprs = UrlParameter.Optional, page=1 });
(or, if you prefer, you can change the name of the Expression property of your model).
This often happens working with form fields created by HtmlHelper methods such as TextBoxFor/EditorFor, when the ViewModel has one or more properties that share the same name of a Router/Controller parameter: you can easily check this looking at the generated HTML code, your input-type fields created by the HtmlHelper methods will most likely have an id='Expression' attribute which causes the whole problem.

Detect Route Values in a View

Is it possible to detect a route value in a view?
Such as /pages/create/1 and I want to check to see if 1 is there?
Basically, I want to render a different partial view based on this value though I'm fairly sure this is probably not the best way to go about what I'm trying to achieve.
On a side note, instead of doing the above is it possible for me to be able to change what partial views are rendered in a view based on a value from within my controller?
ViewContext.RouteData.Values["whatever"]
You can inspect a RouteData object through ViewPage.ViewContext.RouteData. Then check for values using something like
string areaname = routeData.Values["area"] as string;
string controllername = routeData.Values["controller"] as string;
string actionname = routeData.Values["action"] as string;
string id = routeData.Values["id"] as string;
If you find that you want to inspect these values in the controller instead, you can access them using ControllerBase.ControllerContext.RouteData. Something similar applies for action filters etc.
Other answers are correct, but thought i'd address your last sentence:
On a side note, instead of doing the above is it possible for me to be able to change what partial views are rendered in a view based on a value from within my controller?
Well partial view's are rendered in the View itself (unless calling from JavaScript and binding directly to DOM) with the following code:
<%: Html.RenderPartial("SomePartial") %>
So to prevent "code soup" (if statements) in your view, you use a HTML helper which calls through to RenderPartial after inspecting the ViewContext:
public static string RenderCustomPartial(this HtmlHelper helper, RouteData rd)
{
string partialName;
if (rd.Values["SomeParam"] == 1)
partialName = "PartialOneName";
else
partialName = "PartialTwoName";
return helper.RenderPartial(partialName);
}
And then in the View:
<%: Html.RenderCustomPartial(ViewContext.RouteData) %>
You could make some mods to the above - like access the route data directly in the extension, pass through the model to bind in the partial, etc - but you get the idea.
Alternatively you could do the above IF statement in your controller, and stuff the partial view name in the ViewData, then use that in the regular RenderPartial call in your View.
Whatever floats your boat. :)

ASP.net MVC site.master link using HTML.ActionLink

I have the following code in my site.master for a menu:
<ul id="menu">
<li><%= Html.ActionLink("My Contact Info", "DetailsbyUserName/" + Html.Encode(Page.User.Identity.Name), "Users")%></li>
</ul>
When I hover over the URL I see that it points to:
http://site/Users/DetailbyUserName/[name]
which is correct.
The issue is that when I put a breakpoint in the Users controller class below:
public ActionResult DetailsbyUserName(string loginName)
{
UserInfo user = repo.GetUserByName(loginName);
return View(user);
}
it seems that the loginName parameter is always null.
Any suggestions?
The problem is that you don't have a Route set up to specify a parameter called "loginName".
I'm guessing that your default Route is swallowing your request, and trying to assign the [name] value to a parameter called "id". If you change the name of the parameter to "id" from "loginName", I bet it will work for you.
Remember that the routing engine maps each URL segment to a named parameter. The default route looks like: "{controller}/{action}/{id}". If you want to have a parameter named "loginName", you would have to come up with a Route that had the segments "{controller}/{action}/{loginName}", that was different from the default route, so that the default route didn't match it first.
try:
Html.ActionLink("My Contact Info", "DetailsbyUserName", "Users", new { loginName = Html.Encode(Page.User.Identity.Name), null);
if your controller is something like this:
public ActionResult DetailsbyUserName(string veryVeryLongParameterName);
I guess you have to use new { veryVeryLongParameterName = "YourParamValue" } in the ActionLink's routeValues parameter.
And also, you need a route for that.
I'm very new to this too, at least that's what I understood about ActionLinks, hope someone can explain it better.

ASP.NET MVC routing

Up until now I've been able to get away with using the default routing that came with ASP.NET MVC. Unfortunately, now that I'm branching out into more complex routes, I'm struggling to wrap my head around how to get this to work.
A simple example I'm trying to get is to have the path /User/{UserID}/Items to map to the User controller's Items function. Can anyone tell me what I'm doing wrong with my routing here?
routes.MapRoute("UserItems", "User/{UserID}/Items",
new {controller = "User", action = "Items"});
And on my aspx page
Html.ActionLink("Items", "UserItems", new { UserID = 1 })
Going by the MVC Preview 4 code I have in front of me the overload for Html.ActionLink() you are using is this one:
public string ActionLink(string linkText, string actionName, object values);
Note how the second parameter is the actionName not the routeName.
As such, try:
Html.ActionLink("Items", "Items", new { UserID = 1 })
Alternatively, try:
Items
Can you post more information? What URL is the aspx page generating in the link? It could be because of the order of your routes definition. I think you need your route to be declared before the default route.
Firstly start with looking at what URL it generates and checking it with Phil Haack's route debug library. It will clear lots of things up.
If you're having a bunch of routes you might want to consider naming your routes and using named routing. It will make your intent more clear when you re-visit your code and it can potentially improve parsing speed.
Furthermore (and this is purely a personal opinion) I like to generate my links somewhere at the start of the page in strings and then put those strings in my HTML. It's a tiny overhead but makes the code much more readable in my opinion. Furthermore if you have or repeated links, you have to generate them only once.
I prefer to put
<% string action = Url.RouteUrl("NamedRoute", new
{ controller="User",
action="Items",
UserID=1});%>
and later on write
link
Html.ActionLink("Items", "User", new { UserID = 1 })

Resources