How to pass model item to #Html.ActionLink text...
#Html.ActionLink( #item.GetLink(),"Controller", "Action" )
this isn't working, if i put it in " " it becomes string. Any ideas?
#Html.ActionLink(item.GetLink(), "Action", "Controller")
(without # character in first parameter, and swap action and controller)
Adding to Evgeny Levin's answer, you could also download the MvcContrib library and strongly type your ActionLinks like so
#Html.ActionLink<Controller>(x=>x.Action(), item.GetLink())
I prefer this method over magic strings, and then in my solution configurations I create a new one that mimics Debug but has the value for MvcBuildViews set to true, and then when you compile, if you have any invalid Links to controller/action pairs, it throws a compiler error. Has saved me many times of pushing out code that doesn't pass required Action parameters
Related
I have many actions which are used HttpPostedFileWrapper, I want to change the space " " to dash "-" in all of the filenames before call the HttpPostedFileWrapper.filename,
I can do it one by one in all of my actions but they are many,
is there any way to change that on somewhere else? in onActionExecuting in filters for example,
Note that override is not a good solution because I have to change the name of input type in all actions again.
In fact, I was in the wrong way because HttpPostedFileWrapper.filename is readonly and can not change it!
ASP.NET c# project... trying to do a very simple page route.
Please note that I know this is NOT actually doing any dynamic routing... I have the id hard coded like this for a reason.
Example:
RouteTable.Routes.MapPageRoute("Test", "ABC", "~/Test.aspx?id=101");
I can browse to http://www.mysite.com/ABC no problems, the page Test.aspx loads, the routing is working as expected.
BUT... where has my id=101 gone?
Request.QueryString["id"] \\ is null...
Page.RouteData.Values["id"] \\ is null...
How can I get hold of the hard coded id in my target resource for the routing?
I got it working by passing DataTokens.
In my real world scenario I don't know what the URL parameters will be (there could be just the "id" like in my question... or there could be others, sometimes none), so I have to do the following:
First check to see if there is a "?" character in the routing target... if there is, then:
Run the string after the "?" character through HttpUtility.ParseQueryString
Then, loop through that collection and add them to a System.Web.Routing.RouteValueDictionary
Then finally add the route, with the DataTokens property set to the RouteValueDictionary
So I have two URLs that I need separate routes for:
/find-your-new-home/155-detroit/1234-some-value
and
/find-your-new-home/155-detroit/5555g-some-other-flipping-value
The route I have in place to handle #1 is:
routes.MapRoute(
"FindYourNewHomeCommunity",
"find-your-new-home/{market}/{community}.html",
new { controller = "Community", action = "Detail" }
);
I have an action filter on the "Detail" method that splits "155" from "detroit" and also splits "1234" from "some-flipping-value" and passes just the ID's to the action method (the id's are all that matter, the text values are inconsequential).
Now, the second route is almost exactly the same EXCEPT that there is a "g" after the "5555" value. This "g" indicates that it should call another action method ("CommunityGroup") on the Community Controller.
My question is: how do I configure two routes to handle these separately? I tried:
routes.MapRoute(
"FindYourNewHomeCommunityGroup",
"find-your-new-home/{market}/{communityId}g-{community}.html",
new { controller = "Community", action = "CommunityGroup" }
);
That doesn't work however, for two reasons:
1) Both URLs end up matching both routes as proven by Phil Haack's RouteDebugger.
2) Because of greedy matching (and this is why I used the text "flipping-value" in the sample URL), communityId ends up containing "5555-some-other-flippin" since it matches to the last occurrence of "g-" in the URL, which happens to be in the "flipping-value" text.
So my question is, how do I get a different action method to fire for these URLs?
EDIT: Just to clarify, these URLs are pre-defined by some other constraints I'm working in and cannot be changed. They have to follow exactly this format.
You could try creating a route constraint so that the "communityId" part of the route would only match numeric characters, eg:
routes.MapRoute(
"FindYourNewHomeCommunityGroup",
"find-your-new-home/{market}/{communityId}g-{community}.html",
new { controller = "Community", action = "CommunityGroup" },
new { communityId = #"\d+" }
);
If you can generate a list of communities, then you can write a custom regex constraint that will look up the communities at runtime and find them for you. It will then construct the constraint to only catch routes that match what you're looking for for the communities. One potential problem with it is that it only works on Application_Start. It'd be nice to change it dynamically. You could probably do it by updating the RouteTable.Routes at runtime when there are changes, but that is hacky.
Anyway, I ended up answering another Stack Overflow question that covered the same ground, here's the answer I wrote there.
This is NOT a question about how to correctly bind a series of check boxes to a model property (a common question) - my site works perfectly reading checkbox values from a request, either POST or GET + query string.
This is about how to use Html.ActionLink to generate a link that formats multiple checkbox values correctly in the query string.
So I have the following model:
public class ModelType
{
public string[] V { get; set; }
}
And I bind, say, 3 checkboxes to that model in the view as I have three possible values (and, yes, combinations of said values).
Here is the final Html
<INPUT id="chk1" value="1" type="checkbox" name="V">
<INPUT id="chk2" value="2" type="checkbox" name="V">
<INPUT id="chk3" value="3" type="checkbox" name="V">
If all three values are checked when the form is submitted to the server as a GET then the generated query string is, of course, ?V=1&V=2&V=3.
Model binding works beautifully, and everything is happy.
However, let's say I want to produce a link to the same action, passing either an anonymous type or RouteValueDictionary to produce the same query string; logic states that you would do something like this:
Version 1: With an instance of ModelType as the model on the view:
<%= Html.ActionLink("Test link", null /* action name */, new { V = Model.V }) %>
Version 2: Initialising the 'V' member directly as an array:
<%= Html.ActionLink("Test link", null /* action name */,
new { V = new string[] { "1", "2", "3" } }) %>
[In the question I say RouteValueDictionary because in both cases both of these anonymous types are getting turned into such before link generation occurs.]
In both cases the query string that is produced is as follows: ?V=System.String%5B%5D.
Now, I understand that this is because the link generator is simply calling ToString() on the array; but it would seem that there is no way to pass MVC a value that will cause it to generate ?V=1&V=2&V=3.
Since neither the language lets us do:
new { V="1", V="2", ... }
Nor RouteValueDictionary lets us do:
d["V"] = "1"; d["V"] = "2"; ...
I've also tried the desperate measure of pre-processing a RouteValueDictionary, so that for each IEnumerable<string>, I produce a single string with each value separated by a comma, and then write that string back - so the query string ends up like this: ?V=1%2C2%2C3.
But of course MVC doesn't automatically bind such a string to an array (you just get one string in the array with the commas in it); so each property that will be bound in this way has to be custom bound - which, for me, just seems like a few too many hoops to be jumping through.
So it seems that I'm stuck.
Am I missing something? Will I need to write special versions of ActionLink that do what I want?
Any help, as always, greatly appreciated.
So I've gone and researched this and this is my view. I hope somebody can come up with a more optimistic answer, but somehow I think not.
Following the Html.ActionLink extension method all the way through, the core URL that it produces is generated by the (cough)sealed and (cough cough!)internal class System.Web.Mvc.ParsedRoute and it's method Bind in the System.Web.Routing assembly.
At the very end of this method (this is a Reflector listing) - this is how it generates the query string:
if (unusedNewValues.Count > 0)
{
bool flag5 = true;
foreach (string str2 in unusedNewValues)
{
object obj5;
if (acceptedValues.TryGetValue(str2, out obj5))
{
builder.Append(flag5 ? '?' : '&');
flag5 = false;
builder.Append(Uri.EscapeDataString(str2));
builder.Append('=');
builder.Append(Uri.EscapeDataString(
Convert.ToString(obj5, CultureInfo.InvariantCulture)));
}
}
}
I should mention first that unusedNewValues is a HashSet<string> which means - unfortunately - that it will only ever render one name/value pairing.
You can't fudge the data string with embedded a=b&a=c(...) strings because the string value is escaped - thus it is not possible to generate such query strings in Mvc at all unless you replace the routing framework.
As a result, an alternative will be to write a collection data type which does something like the following (this is a dirty implementation):
public class StringCollection : List<string>
{
public override string ToString()
{
//use the pipe character as a delimiter - but this doesn't work
//if the strings being carried around ccould naturally contain '|'!
return string.Join("|", this.ToArray());
}
}
And then implement a custom model binder (deriving from DefaultModelBinder) for the type that first uses the default method and then checks to see if the resulting collection only has one string which has '|' characters in it. If so, it then post-processes the collection, replacing its contents with the expanded values from the single string.
This is horrid - but the least nasty of all the solutions I can currently think of.
Just now have to make a load of changes to all my existing model types that use collections of strings that might be bound to query string values...
Had a similar problem recently. I ended up ditching the HtmlHelper/RouteValueDictionary and just constructed my URL from scratch. Something like:
Link
May not appeal to everyone's tastes, but it worked for me.
I've got a route that looks like this:
routes.MapRoute(
"BlogTags",
"Blog/Tags/{tag}",
new { controller = "Blog", action = "BrowseTag", viewRss = false }
);
And I create a URL using that route like this:
<%= Html.RouteLink(Html.Encode(sortedTags[i].Tag),
new { action = "BrowseTag", tag = sortedTags[i].Tag })%>
However, when a tag with a # character (like "C#") is used, the routing engine doesn't escape it, so I get a URL that looks like this:
C#
What I need is the # escaped so that it looks like this:
C#
I tried doing a Url.Encode on the tag before it went into the route, like this:
<%= Html.RouteLink(Html.Encode(sortedTags[i].Tag),
new { action = "BrowseTag", tag = Url.Encode(sortedTags[i].Tag) })%>
But that makes the routing engine double escape the # (which causes an ASP.NET crash with a bad request error)):
C#
How can I get the routing engine to escape that # character for me correctly?
Thank you for your help in advance.
As a very bald solution, I would manually replace "#" with "%23" in the output of RouteLink. Provided you don't use fragments in your urls, it should work.
You could use regular expression to only apply replace to the last part of your url.
I have a similar SO question relating to "/". While researching that issue I learned that ASP.NET decodes the URL values before they get passed to the MVC framework, and since "#" has special meaning to URLs (just like the "/" I was dealing with) there's a good chance that something in the base routing engine is causing this behavior.
Like Levi mentioned in his comment, one solution is to use ASP.NET 4.0. Another solution would be to write a RouteLink helper that automatically replaces "#" with some marker (like "!MY_HASH_TOKEN!") and then reverse that replacement in your controller (or perhaps via a HttpModule of some sort).
Or, just throw in the towel and pass the tag value as a querystring argument. Not as sexy, but its simple and it works.