I have a question regarding RouteLink vs. ActionLink.
Consider the following route
routes.MapRoute("Routename1",
"{someEnum}/SpecificAction/{id}/{stringId}",
new { controller = "MyController", id = (int?)null, stringId= (string)null, action = "SpecificAction" },
new { someEnum= "(EnumVal1|EnumVal2)" }
);
The weird {someEnum} part is because I use a general controller for all values of an enum that form the typical controller part of a url. For instance, /EnumVal1/Action/ and /EnumVal2/Action/ use the same controller. That's not part of the problem, however.
Consider the following two ways of linking:
<%=Html.RouteLink("Click me","Routename1", new { id = 32, stringId = "Yatzy" })%>
<%=Html.ActionLink("Click me", "SpecificAction", "EnumVal1", new { id = 32, stringId = "Yatsy" }, null)%>
The RouteLink generates the correct url, which would be /EnumVal1/SpecificAction/32/Yatzy
The ActionLink generates an url that looks like /EnumVal1/SpecificAction/32?stringId=Yatzy
Why is this? Could someone explain this to me, please.
RouteLink can only ever use the one route you specify. ActionLink will use the first matching route, whether it's the one you intended or not. Your two examples are probably matching different routes.
Phil Haack's routing debugger would help clarify this.
Related
I'm busy with an mvc 5 application. I have a list of names from a database which are displayed in html. I filter the names alphabetically using html.actionlink for A, B, C, D ...Z. Through each html.actionlink I pass through each letter as an Id parameter and then in my controller I filter which names are returned using .ToList() by finding .Where() the first letter of my names match the Id parameter.
That is all working well. What I need to do now is that if there are no names which begin with a certain letter then that letter must be grayed out in the view.
How can I add a class to an html element through my controller? I need to make sure that if there are no names with a certain letter then my html link must have css class with color: grey. I don't know which names there will be because the database is populated by an administrator.
You can define your CSS class and apply your class in html helpers. Like this:
.yourClassName
{
color:grey;
}
Applying your class:
#Html.ActionLink("Name", "{Controller}", null,new { #class ="yourClassName" })
Not too sure I follow the design flow in your question but I think this code may help you.
#if(model.Names.Where(x => x.StartsWith("L").Count() != 0)
{
#Html.ActionLink("L", "{Controller}", "{Action}", null, new {} { #class = "{NOT GRAY}"})
}
#else
{
#Html.ActionLink("L", "{Controller}", "{Action}", null, new {} { #class = "grayed"})
}
Basically, you can write an IF statement in Razor syntax and then check and see if the incoming data is empty by doing a Count and then styling the element differently for each case.
Because I don't know what name you are using for your model, what classes your applying to your not grayed elements, controller names, action names, then you will need to edit to this code to get it to work.
Using ActionLink function you can pass an anonymous object with entries corresponding to Html attributes.
Look at the parameter of type object, called htmlAttributes in the ActionLink function.
Here is an example (note that class is prefixed with # because it is a keyword) :
#Html.ActionLink(
"Portfolio",
"Index",
"Portfolio",
routeValues: null,
htmlAttributes: new { #class = "grayed" }
)
I have a list of products with some title field, e.g.:
ID Title
1 TShirts CK
2 Brand new books
3 Selling whatever I have useless
Ok, I'm calling a Detail method this way:
<a href='#Url.Action("Detail", "Products", new { productId = 3 })'>See detail</a>
[Route("detail/{productId:int}")]
public ViewResult Detail(int productId) {
//...
return View();
}
The generated URL is:
http:example.com/products/detail/3
Well, the idea I want is show an URL like this:
http://example.com/products/detail/3/selling-wathever-i-have-useless
Is there a nice and clean way to do this, based on the given scenario?
I believe this has been termed a URL slug, which will make it 100x easier to search for. I suggest you start: https://stackoverflow.com/a/2921135/507025 for an algorithm to help you slugify your url.
If you have a database with the information, you might want to save the slugified description to it so you can check for duplicates upon the creation of a new item.
Routing will be very similar although you'll be needing to change your routing attribute to:
[Route("detail/{productName:string")]
public ViewResult Detail(string productName)
{
return View();
}
You can then search your DB for the slugified description to return the item searched for OR use the terms given in a search to return multiple results.
There are probably different ways of doing this but now that you know it's called 'slug' you will have an easier time finding info about them.
You can have a route as below and when you define Url.Action you can pass product title to some kind of method which convert the given text to URL friendly text.
Example
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{productId}/{title}",
defaults: new { controller = "Home", action = "Index", productId = UrlParameter.Optional, title = UrlParameter.Optional }
);
<a href='#Url.Action("Index", "Home", new { productId = product.ID, title = ToFriendlyUrl(product.Title) })'>See detail</a>
public ViewResult Detail(int productId, string title)
{
// ...
return View();
}
Thanks!
Why would this route result in 404 Not Found when hitting url /users/3 to call upon the route with a page number only in ASP.NET MVC:
routes.MapRoute(
"Users", // Route name
"users/{page}/{sortColumn}/{sortDirection}", // URL with parameters
new { controller = "User", action = "Index", page = UrlParameter.Optional, sortColumn = UrlParameter.Optional, sortDirection = UrlParameter.Optional }, // Parameter defaults
new { page = #"\d+", sortColumn = #"[\w-]+", sortDirection = #"asc|desc" } // Route constraints
);
Shouldn't it be okay for me to load the route simply with the page parameter specified, as the sortColumn and sortDirection parameters are both set to UrlParameter.Optional?
Update:
Ok after digging up another StackOverflow answer on a similar topic, it appears if you choose to have an optional route, the constraint must also be optional. So changing my route constraints to:
new { page = #"\d*", sortColumn = #"[\w-]*", sortDirection = #"(asc|desc)?" }
which simply tests for zero or more matches makes my pages load up on all accounts.
However, if I try to make a route link that reflects that (#Html.RouteLink("test 2", "Users", new { page = 2 })), the hyperlink is generated as /users not /users/2! Strange thing though, if I manually hit /users/2 then the RouteLink is written as /users/2 lol Argh! :)
Perhaps I do have to make separate routes, but this is really not desirable as I don't want to have to call upon differently named routes in Html.RouteLink()....
Thoughts?
Because you provide two parameters, but it expects 3 optional ones.. so it does not know which one is missing ...
In general you cannot use two consecutive optional parameters.
References
http://weblogs.asp.net/imranbaloch/archive/2010/12/26/routing-issue-in-asp-net-mvc-3-rc-2.aspx
http://connect.microsoft.com/VisualStudio/feedback/details/630568/url-routing-with-two-optional-parameters-unspecified-fails-on-asp-net-mvc3-rc2
I have the following code:
Url.Action("Index", "Home", new {param1 = Model.Param1, param2 = Model.Param2} )
Now, if Param2 is NullOrEmpty I want to link using the following code:
Url.Action("Index", "Home", new {param1 = Model.Param1} )
Is there an easier way to do this or will I need to do something like this:
string myLinkVal = (Model.Param2 == string.IsNullOrEmpty()) ?
Url.Action("Index", "Home", new {param1 = Model.Param1} ) :
Url.Action("Index", "Home", new {param1 = Model.Param1, param2 = Model.Param2} );
EDIT:
Thank you for your answer. The Problem is, that I have the empty param in my link.
I have the following routes:
"/{param1}/{param2}/{controller}/{action}/{id}"
...
"/{param1}/{controller}/{action}/{id}"
When Param2 is empty I want to use the other route (the one without param2).
Currently I can only archieve this by using
new {param1 = Model.Param1, param2 = Model.Param2}
or
new {param1 = Model.Param1}
Then the correct route is picked.
I ran a couple quick test, and assuming Model.Param2 is a string (which it looks like it is by your example code), then MVC already handles this.
<%= Html.ActionLink("Click Me", "TestAction", new { id = 5, name="" })%>
<%= Html.ActionLink("Click Me", "TestAction", new { id = 5 })%>
Both of the above result in the same URL being rendered.
For completeness, here is what my action method looks like (at least the signature):
public ActionResult TestAction(int id, string name) {...}
HTH,
Brian
You could try constructing your object parameter list in your controller, and then pass that as part of the viewModel
Url.Action("Index", "Home", Model.ParamList);
Where ParamList is just an object. It wouldn't reduce your if/else logic any, but it would remove said logic from your View.
I have a custom route without constraints that generates a Restful URL with an ActionLink.
Route -
routes.MapRoute(
"Blog", // Route name
"Blog/{d}/{m}/{y}", // URL with parameters,
new { controller = "Blog", action = "Retrieve" }
Generates -
http://localhost:2875/Blog/12/1/2010
From -
<%=Html.ActionLink("Blog Entry - 12/01/2010", "Retrieve", "Blog", new { d = 12, m = 01, y = 2010 }, null)%>
If I add constraints like so.
routes.MapRoute(
"Blog", // Route name
"Blog/{d}/{m}/{y}", // URL with parameters,
new { controller = "Blog", action = "Retrieve" },
new { d = #"\d{2}", m = #"\d{2}", y = #"\d{4}" }
It generates -
http://localhost:2875/Blog/Retrieve?d=12&m=1&y=2010
Extra information: it is added before the custom route.
Any ideas?
Cheers
I was working on the same issue while writing my blog.. In the end I realised that my Urls will have to use 1 digit month numbers.. change your route definition to this, and it will work:
routes.MapRoute(
"Blog", // Route name
"Blog/{d}/{m}/{y}", // URL with parameters,
new { controller = "Blog", action = "Retrieve" },
new { d = #"\d{1,2}", m = #"\d{1,2}", y = #"\d{4}" }
Or you can pass 2 digit strings as your day/month route values.. but you might miss this in some places and have dead links, so I'd recommend the route constraints fix..
If you DO find a workaround - drop me a mail pls ^_^
Artiom is essentially right. Since your ActionLink code specifies single digit integers in the route values, the single digit fails against your constraint. So, you can either change the constraint as Artiom suggests, or slightly modify the ActionLink code so the route values are "strings" (in double quotes):
Html.ActionLink("Blog Entry - 12/01/2010", "Retrieve", "Blog", new { d = "12", m = "01", y = "2010" }, null)