Use Object Name as Link with ActionLink, Not Hard-coded Literal - c#

I'm trying to get my WebGrid to use the name of my entity as a link. If I just do this:
grid.Column("Name"),
The grid displays the name of the entity in each row of the grid:
However, I want the name to appear as a link. The closest I've come to getting this working is doing this:
grid.Column("Name", format: (item) => #Html.ActionLink("Edit", "Edit", new { id = item.Id })),
But, as you can see, every name is Edit. How can I get the actual object name there? I tried this, but I get an error (the only difference is that I'm trying to use item.Name in place of "Edit" as the first parameter of the ActionLink method):
grid.Column("Name", format: (item) => #Html.ActionLink(item.Name, "Edit", new { id = item.Id })),
Error: TrackerJob>>' has no applicable method named 'ActionLink' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.

format is func which have input parameter of type dynamic and as result type of item.Name is dynamic too at compile time. And as error says use following code :
grid.Column("Name", format: (item) => #Html.ActionLink((string)item.Name, "Edit", new { id = item.Id })

Try swapping the arguments around?
grid.Column("Name", format: (item) => #Html.ActionLink("Edit", item.Name, new { id = item.Id }))
After First Comment
Try using an overload of ActionLink with more parameters (from here):
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
RouteValueDictionary routeValues,
IDictionary<string, Object> htmlAttributes
)
As an overload can become ambiguous with just strings. Alternatively use named parameters when defining the ActionLink.

Tried looking into GetSelectLink()? More on it here: http://weblogs.asp.net/andrebaltieri/archive/2010/11/02/asp-net-mvc-3-working-with-webgrid-part-2.aspx
Personally, I would stick with making the tables yourself - when they get more customized, I find it less confusing, but that is my opinion.
EDIT:
You could do something like this, but again I stress that you are kind of getting away from the point of this being 'simple':
item.GetSelectLink(String.Format("<a href='{0}'>{1}</a>", Url.Action("Edit", new { id = item.Id }), item.Name))

Related

Razor won't compile even though signature matches exactly for EditorFor

I am using MVC 5.2, I'm working in a partial view that I use multiple times in a view to bind to a list of objects in my view model.
In order to get it to bind properly my understanding is that the names of the html objects need to look like Model[x].Property. The only Signature I can find for EditorFor that allows me to do this while maintaining ability to add html attributes is
public static MvcHtmlString EditorFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression,
string templateName,
string htmlFieldName,
object additionalViewData
)
My problem is if I try to concatenate anything in the htmlFieldName field it tells me there are invalid arguments. If I use a plain string with not concatenation, works fine. I have tried all types of concatenation, below is 1 example I've tried.
#Html.EditorFor(model => model.Name,"", #String.Format("Contacts[{0}].Name",ViewBag.Id), new { htmlAttributes = new { #class = "form-control" } })
HtmlHelper' does not contain a definition for 'EditorFor' and the best extension method overload 'EditorExtensions.EditorFor(HtmlHelper, Expression>, string, string, object)
Am I trying to accomplish this in the wrong way? Is there a better way to bind to a list of objects? Also how do I maintain things like regex validation, it doesn't seem to work anymore once I change the name.
Casting the dynamic ViewBag.Id satisfies the Razor compiler and the error goes away.
#Html.EditorFor(model => model.Name,
"",
String.Format("Contacts[{0}].Name", ViewBag.Id as string),
new { htmlAttributes = new { #class = "form-control" } }
)

How to pass routeValues that contains hyphen via actionlink in asp.net mvc 5

I have an actionlink in view, I need it to pass parameter containing hyphen (-). Changing the name convention is not possible. How do I do this?
<li>#Html.ActionLink("abc", "abc", "abc", new { area = "",sap-ie="Edge" }, new { id = nav_abc"})</li>
This one gives me an error "Invalid Anonymous Type Declarator" since it contains hyphen. Basically I need the actionlink to generate html like below.
<a href=".../abc?sap-ie=Edge" id="nav_abc" >abc</a>
If you are sure the url won't change much from the structure you've specified you could use an anchor tag and build the url from Url.Action.
<a href='#Url.Action("abc", "abc")?sap-ie=Edge'>abc</a>
The whole point of htmlhelpers are to help generate html anyway .. so if its getting in the way, you can drop down to html proper and just get it done.
Just wanted to point out that it's not that the underscore trick only works with data attributes, it's that it only works with passing HTML attributes in general. This is because it makes sense to change underscores to hyphens in the context of HTML, as underscores aren't use in HTML attributes. However, it's perfectly valid for you to have a route param with an underscore, so the framework can make no assumptions about your intent.
If you need to pass route values with hyphens, you have to use a RouteValueDictionary. This is simply a limitation of anonymous objects that can't be overcome.
<li>#Html.ActionLink("abc", "abc", "abc", new RouteValueDictionary { { "area", "" }, "sap-ie", "Edge" } }, new RouteValueDictionary { { "id", "nav_abc" } })</li>
Unfortunately, there's no ActionLink overload that accepts both a RouteValueDictionary for routeValues and an anonymous object for htmlAttributes, so switching one means switching both. You can technically use any IDictionary implementation for the htmlAttributes param, so you may prefer to use just new Dictionary { { "id", "nav_abc" } }. It's up to you.
Have you tried using this overload of the ActionLink method?
#{
var routeValues = new RouteValueDictionary();
routeValues.Add("sap-ie", "Edge");
routeValues.Add("area", "");
var attributes = new Dictionary<string, object>();
attributes.Add("Id", "nav_abc");
}
#Html.ActionLink("Go to contact page", "Contact", routeValues, attributes)

Pass model property to Html.ActionLink

Edit: I found the answer, but it meant abandoning the use of HtmlActionlink in favor of doing this (the "blah" parameters from my original post now being subbed in for what I actually needed):
<a href="#Url.Action("LinkedDetails",
new
{
controller = "LGDetails",
findByString = item.AccounNumber
})">#item.LastName</a>
This does exactly what I want. Trying to accomplish the same thing with HtmlActionLink results in one error or another no matter what I tried:
#Html.ActionLink(item.LastName,
"../LGDetails/LinkedDetails",
new
{
controller = "LinkedDetails",
findByString = item.AccounNumber
})
result: Value cannot be null or empty.
Parameter name: linkText
Trying like this:
#Html.ActionLink(Model.LastName .....
Result:
System.Collections.Generic.IEnumerable' does not contain a definition for 'LastName' and no extension method 'LastName' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?)
etc etc. Always one of those errors regardless.
And yes, I tried every single thing suggested here, and an equal number of other things found elsewhere (which is how I found the answer I did use, it was posted by another person having the same problem).
Despite a lot of searching, I can't seem to figure out how to do what seems like it should be basic.
This works fine:
#Html.DisplayFor(modelItem => item.LastName)
But I want to display the LastName field as the linked text inside an Html.ActionLink like this:
#Html.ActionLink(item.LastName, "blah", "blah")
But nothing works. Doing it like the above gives me a compilation error:
The type arguments for method
'System.Web.Mvc.Html.DisplayExtensions.DisplayFor(System.Web.Mvc.HtmlHelper,
System.Linq.Expressions.Expression>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly
I've tried it like this:
#Html.ActionLink(#Html.DisplayFor(modelItem => item.LastName), "blah", "blah")
and other variations thereof where it is nested. That doesn't work either (won't compile) with the error "no overload method for actionlink.."
I feel like this should be simple. Yes, I'm very new to C# and MVC.
you need to use this overload of Html.ActionLink:
#Html.ActionLink(string LinkText,string ActionName,string ControllerName)
do like this:
#Html.ActionLink(Model.LastName, "ActionName","ControllerName")
Updated:
then do like this, as your Model is IEnumerable:
#foreach(var item in Model)
{
#Html.ActionLink(item.LastName, "ActionName","ControllerName")
}
If Model is Single like this:
#model MyCompany.LGCustData
then it should be lke this:
#Html.ActionLink(Model.LastName, "ActionName","ControllerName")
#Html.ActionLink(#Model.AccounNumber....) works in case someone ends up here just like me.
this is a breaf description of how you should use an actionlink
Html.ActionLink("link title",
"ActionMethod",
"ControllerName",
null, // <-- Route arguments.
null // <-- htmlArguments
)
try something like this:
#Html.DisplayFor(Model.LastName, "actionMethod","controllerName",new{Model.LastName});
*if the case you want to add some parameters to your route, following the standard convention:
/controller/action/parameter
otherwise
#Html.DisplayFor(Model.LastName, "actionMethod","controllerName");
For me I simply convert the "dynamic text" before in a string with String.Format() function like this
<ul>
#foreach (var user in ViewBag.users )
{
string complete_name = String.Format("{0} {1}", user.Firstname, user.Lastname);
<li>#Html.ActionLink(complete_name, "Details", new { Id = user.Id })</li>
}
</ul>
On my computer it works very fine.
Just use
#Html.ActionLink(Html.DisplayNameFor(model => model.LastName).ToHtmlString(), "blah", "blah")

Passing an array to Html.ActionLink()

I followed this example:
ASP.NET MVC - Pass array object as a route value within Html.ActionLink(...)
But, my Action is always called with null. What am I doing wrong?
foreach (OrderDetail od in order.OrderDetails)
{
rvd.Add("key" + count++, productID);
rvd.Add("key" + count++, productName);
}
#Html.ActionLink(linkText, "Renew", "Orders", rvd, new Dictionary<string, object>())
The query string is correctly generated, like ?key0=dog&key1=cat&key2=fish..., but I get a null parameter in my Action below:
public ActionResult Renew(RouteValueDictionary rvd)
{
// 'rvd' is null here!
}
Please note: I don't know the number of parameters in advance.
The query string is correctly generated, like ?key0=dog&key1=cat&key2=fish...
No, this is not a correct url. A correct url would have looked like this:
?%5B0%5D.Key=123&%5B0%5D.Value=dog&%5B1%5D.Key=456&%5B1%5D.Value=cat...
which would have mapped to:
public ActionResult Renew(Dictionary<int, string> rvd)
{
...
}
You could write a custom ActionLink to generate this url:
public static class LinkExtensions
{
public static IHtmlString MyActionLink(
this HtmlHelper html,
string linkText,
string actionName,
string controllerName,
IDictionary<string, string> parameters
)
{
var a = new TagBuilder("a");
var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
var query = string.Join("&", parameters.Select((x, i) => string.Format("[{0}].Key={1}&[{0}].Value={2}", i, urlHelper.Encode(x.Key), urlHelper.Encode(x.Value))));
var url = string.Format(
"{0}?{1}",
urlHelper.Action(actionName, controllerName, null, html.ViewContext.HttpContext.Request.Url.Scheme),
query
);
a.Attributes["href"] = url;
a.SetInnerText(linkText);
return new HtmlString(a.ToString());
}
}
which you could use like this in your view:
#Html.MyActionLink(
linkText,
"Renew",
"Orders",
order.OrderDetails.ToDictionary(x => x.ProductID.ToString(), x => x.ProductName)
)
You can read more about the correct wire format for binding to various collections in this blog post.
I imagine what is happening is you are expecting the model binder to bind your array to a RouteValueDictionary, but the model binder doesn't know that key0=dog&key1=cat&key2=fish is supposed to be a dictionary. I would recommend changing your code to accept a string array. To do this, your query string needs to look something like this: ?rvd=dog&rvd=cat&rvd=fish
And your Action...
public ActionResult Renew(string[] rvd)
{
// 'rvd' is no longer null here!
}
The important part is rvd is the parameter name in your action, as well as the name of each element in the querystring: ?rvd=dog&rvd=cat&rvd=fish. If you really want to use a dictionary instead of a string array, then your querystring should look like this: ?rvd[0]=dog&rvd[1]=cat&rvd[2]=fish, giving each item an array index, but you would probably have to change your parameter from RouteValueDictionary to Dictionary<string,string>, I'm not quite sure. More info here. EDIT: See Darin's comment about binding to a dictionary, as I believe his is correct.
You may have to write your own extension for Html.ActionLink that accepts an array (or whatever OrderDetails is) and creates the querystring as an array. This looks like a pretty good starting place.

How to dynamically create routes in a HTML actionlink

I am trying to send the route values to a method but I cant seem to figure this out. Here is my code
<% string s = "cool";
object d = new { s = "1" };
%>
<%= Html.ActionLink("Home", "Index", d, "ql")%>
The following code produces a url like this
http://localhost:49450/?s=1
the url should be like this
http://localhost:49450/?cool=1
What am I missing
because in the context of a 'new { ... }' expression the 's' does not correspond to a variable as it may first appear - it defines the name of a member of an anonymous class that is created.
when you say :
new { S = 123 }
you are actually generating a class, which is anonymous (you never get to see the name of the class). The type of each member of the class is implicitly determined by whatever you're assigning to it. In the above example a class something like this is generated
class AnonymousClass_S483Ks4 {
public int S {get;set;}
}
There are two ways you can do what you want:
1) you would have to say :
new { cool = 123 }
2)
Now I assume though that you want the name to be dynamic so you need to use RouteValueDictionary which allows you to put key value pairs in.
// RouteValueDictionary is IDictionary<string, object>
var dictionary = new RouteValueDictionary();
string s = "cool";
dictionary.Add(s, 123);
htmlHelper.ActionLink("Home", "Index", dictionary);
As you can see, here you can use a variable 's' to represent whatever you want. This should give you the URL you need.

Categories

Resources