I'm new to MVC and would like to add a link to something like ~/Destinations/35, where it would refer to the Index view of the Destinations controller, and 35 is the ID of the destination to be displayed.
Neither ActionLink() or RouteLink() appear to allow me to create a link such as this.
Also, I tried something like this:
<table>
#foreach (var d in ViewBag.Results)
{
<tr>
<td>
#Html.ActionLink(
String.Format("<b>{0}</b>", #Html.Encode(d.Title)),
"Details", "Destinations")
</td>
</tr>
}
</table>
But I get the following error on the ActionLink line, which I don't understand.
'System.Web.Mvc.HtmlHelper' 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.
Can someone help me create this link?
The first problem with your code is that you are trying to use HTML in the link text (the <b> tags) which is not possible because by design it always HTML encodes.
So assuming you didn't want HTML in the link you could do this:
#Html.ActionLink(d.Title, "Details", "Destinations", new { id = "35" }, null)
And assuming you need HTML inside the anchor you have a couple of possibilities:
Write a custom ActionLink helper which won't HTML encode the text (recommended) and then use like this:
#Html.MyBoldedActionLink(d.Title, "Details", "Destinations", new { id = "35" }, null)
Something along the lines:
<a href="#Url.Action("Details", "Destinations", new { id = "35" })">
<b>#d.Title</b>
</a>
and since I recommend the first approach here's a sample implementation of the custom helper:
public static class HtmlExtensions
{
public static IHtmlString MyBoldedActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
object routeValues,
object htmlAttributes
)
{
var anchor = new TagBuilder("a");
anchor.InnerHtml = string.Format("<b>{0}</b>", htmlHelper.Encode(linkText));
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
anchor.Attributes["href"] = urlHelper.Action(actionName, controllerName, routeValues);
anchor.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return new HtmlString(anchor.ToString());
}
}
Related
I have a simple view like this:
#using MyProject.WebClient.Models
#model LoginClienteModel
<h1>#ViewBag.Title</h1>
#{
<text>
<h2>
<span>Olá #Model.Login . </span>
</h2>
<h3>
<span>Info: (#Model.ProperyOne)</span>
</h3>
</text>
}
<br/>
#Html.ActionLink("Option 1", "OptOne", "Home", new {pdModel = new OptOneModel{Login = Model.Login,Data = DateTime.Now.Date}});
When this view is showed, all data from model is displayed correctly. You can see I've another ActionLink, pointing to action OptOne. That action requires a parameter pdModel of type OptOneModel. As you can see, I instantiate it using current model values.
But when my Action is executed, Login property is null, only Data is not null.
public ActionResult OptOne(OptOneModel pdModel)
{
return View(pdModel); // <-- Here only Data property is not null
}
I'm lost. I can't see what is wrong with that.
Unfortunately, you can't pass a complex type into a route value that way for an ActionLink. Manual invocation of an Action, I believe you can, but not for a link. So:
#Html.ActionLink("Option 1", "OptOne", "Home", new {pdModelId = Model.YourUniqueId, dateRendered = DateTime.Now.Date});
Meanwhile, server side:
public ActionResult OptOne(int pdModelId, DateTime dateRendered)
{
// retrieve model again based on your Id
return View(pdModel);
}
You cannot use hyperlink to pass Model. It only work with Post.
Instead, you can use routeValues to generate query string.
#Html.ActionLink("Option 1", "OptOne", "Home",
new { Model.Login, Data = DateTime.Now.Date }, null)
public ActionResult OptOne(string login, DateTime data)
{
// Do something with login and data.
return View();
}
Use either ActionLink overload variation,
ActionLink(string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
#Html.ActionLink("Option 1", "OptOne", "Home", new OptOneModel{ Login = Model.Login, Data = DateTime.Now.Date}, null);
OR
ActionLink(string linkText, string actionName, object routeValues)
#Html.ActionLink("Option 1", "OptOne", new OptOneModel{ Login = Model.Login, Data = DateTime.Now.Date});
hope this helps.
My view page like this;
#model IEnumerable<Project.ViewModels.ViewModel>
#using Helpers
#foreach (var item in Model)
{
#Html.MyCustomHtmlHelper("test")
}
My custom HtmlHelper like this;
public static MvcHtmlString MyCustomHtmlHelper(this HtmlHelper helper, string TestValue)
{
var builder = new StringBuilder();
builder.Append(TestValue);
return MvcHtmlString.Create(builder.ToString());
}
It works with #model Project.ViewModels.ViewModel but not #model IEnumerable<Project.ViewModels.ViewModel>
My error;
The code you have shown in your question:
#foreach (var item in Model)
{
#Html.MyCustomHtmlHelper("test")
}
doesn't correspond to the code shown in the YSOD:
<td>
#Html.MyCustomHtmlHelper(TestValue)
</td>
It looks like in your real code (which you haven't shown us), you have used some TestValue variable. Unfortunately it's pretty hard to know how/where/of what type is this variable declared but if it is not a string it is more than obvious that your custom helper won't work for the simple reason that this helper expects a string parameter.
Is there anything wrong with this html? I want to have a link in the masterpage to navigate to "CreateParts" view. I have action 'CreateParts' which have a parameter parentPartId in the controller 'PartList'.
<li id="taskAdminPartCreate" runat="server">
<%= Html.ActionLink("Create New Part", "CreateParts", "PartList", new { parentPartId = 0 })%></li>
My controller action is like
public ActionResult CreateParts(int parentPartId)
{
HSPartList objHSPart = new HSPartList();
objHSPart.Id = parentPartId;
return View(objHSPart);
}
When I click on 'Create New Part' in the menu in SiteMaster, I get exception. Please help me out of this.
You are using incorrect overload. You should use this overload
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
Object routeValues,
Object htmlAttributes
)
And the correct code would be
<%= Html.ActionLink("Create New Part", "CreateParts", "PartList", new { parentPartId = 0 }, null)%>
Note that extra parameter at the end.
For the other overloads, visit LinkExtensions.ActionLink Method. As you can see there is no string, string, string, object overload that you are trying to use.
You are using the incorrect overload of ActionLink. Try this
<%= Html.ActionLink("Create New Part", "CreateParts", "PartList", new { parentPartId = 0 }, null)%>
Addition to the accepted answer:
if you are going to use
#Html.ActionLink("LinkName", "ActionName", "ControllerName", new { #id = idValue, #secondParam= = 2 },null)
this will create actionlink where you can't create new custom attribute or style for the link.
However, the 4th parameter in ActionLink extension will solve that problem. Use the 4th parameter for customization in your way.
#Html.ActionLink("LinkName", "ActionName", "ControllerName", new { #id = idValue, #secondParam= = 2 }, new { #class = "btn btn-info", #target = "_blank" })
I am trying to use HTML5 data- attributes in my ASP.NET MVC 1 project. (I am a C# and ASP.NET MVC newbie.)
<%= Html.ActionLink("« Previous", "Search",
new { keyword = Model.Keyword, page = Model.currPage - 1},
new { #class = "prev", data-details = "Some Details" })%>
The "data-details" in the above htmlAttributes give the following error:
CS0746: Invalid anonymous type member declarator. Anonymous type members
must be declared with a member assignment, simple name or member access.
It works when I use data_details, but I guess it need to be starting with "data-" as per the spec.
My questions:
Is there any way to get this working and use HTML5 data attributes with Html.ActionLink or similar Html helpers ?
Is there any other alternative mechanism to attach custom data to an element? This data is to be processed later by JS.
This problem has been addressed in ASP.Net MVC 3. They now automatically convert underscores in html attribute properties to dashes. They got lucky on this one, as underscores are not legal in html attributes, so MVC can confidently imply that you'd like a dash when you use an underscore.
For example:
#Html.TextBoxFor(vm => vm.City, new { data_bind = "foo" })
will render this in MVC 3:
<input data-bind="foo" id="City" name="City" type="text" value="" />
If you're still using an older version of MVC, you can mimic what MVC 3 is doing by creating this static method that I borrowed from MVC3's source code:
public class Foo {
public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes) {
RouteValueDictionary result = new RouteValueDictionary();
if (htmlAttributes != null) {
foreach (System.ComponentModel.PropertyDescriptor property in System.ComponentModel.TypeDescriptor.GetProperties(htmlAttributes)) {
result.Add(property.Name.Replace('_', '-'), property.GetValue(htmlAttributes));
}
}
return result;
}
}
And then you can use it like this:
<%: Html.TextBoxFor(vm => vm.City, Foo.AnonymousObjectToHtmlAttributes(new { data_bind = "foo" })) %>
and this will render the correct data-* attribute:
<input data-bind="foo" id="City" name="City" type="text" value="" />
Update: MVC 3 and newer versions have built-in support for this. See JohnnyO's highly upvoted answer below for recommended solutions.
I do not think there are any immediate helpers for achieving this, but I do have two ideas for you to try:
// 1: pass dictionary instead of anonymous object
<%= Html.ActionLink( "back", "Search",
new { keyword = Model.Keyword, page = Model.currPage - 1},
new Dictionary<string,Object> { {"class","prev"}, {"data-details","yada"} } )%>
// 2: pass custom type decorated with descriptor attributes
public class CustomArgs
{
public CustomArgs( string className, string dataDetails ) { ... }
[DisplayName("class")]
public string Class { get; set; }
[DisplayName("data-details")]
public string DataDetails { get; set; }
}
<%= Html.ActionLink( "back", "Search",
new { keyword = Model.Keyword, page = Model.currPage - 1},
new CustomArgs( "prev", "yada" ) )%>
Just ideas, haven't tested it.
It's even easier than everything suggested above.
Data attributes in MVC which include dashes (-) are catered for with the use of underscore (_).
<%= Html.ActionLink("« Previous", "Search",
new { keyword = Model.Keyword, page = Model.currPage - 1},
new { #class = "prev", data_details = "Some Details" })%>
I see JohnnyO already mentioned this.
In mvc 4 Could be rendered with Underscore(" _ ")
Razor:
#Html.ActionLink("Vote", "#", new { id = item.FileId, }, new { #class = "votes", data_fid = item.FileId, data_jid = item.JudgeID, })
Rendered Html
<a class="votes" data-fid="18587" data-jid="9" href="/Home/%23/18587">Vote</a>
You can implement this with a new Html helper extension function which will then be used similarly to the existing ActionLinks.
public static MvcHtmlString ActionLinkHtml5Data(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes, object htmlDataAttributes)
{
if (string.IsNullOrEmpty(linkText))
{
throw new ArgumentException(string.Empty, "linkText");
}
var html = new RouteValueDictionary(htmlAttributes);
var data = new RouteValueDictionary(htmlDataAttributes);
foreach (var attributes in data)
{
html.Add(string.Format("data-{0}", attributes.Key), attributes.Value);
}
return MvcHtmlString.Create(HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection, linkText, null, actionName, controllerName, new RouteValueDictionary(routeValues), html));
}
And you call it like so ...
<%: Html.ActionLinkHtml5Data("link display", "Action", "Controller", new { id = Model.Id }, new { #class="link" }, new { extra = "some extra info" }) %>
Simples :-)
edit
bit more of a write up here
I ended up using a normal hyperlink along with Url.Action, as in:
<a href='<%= Url.Action("Show", new { controller = "Browse", id = node.Id }) %>'
data-nodeId='<%= node.Id %>'>
<%: node.Name %>
</a>
It's uglier, but you've got a little more control over the a tag, which is sometimes useful in heavily AJAXified sites.
HTH
I do not like use pure "a" tag, too much typing. So I come with solution.
In view it look
<%: Html.ActionLink(node.Name, "Show", "Browse",
Dic.Route("id", node.Id), Dic.New("data-nodeId", node.Id)) %>
Implementation of Dic class
public static class Dic
{
public static Dictionary<string, object> New(params object[] attrs)
{
var res = new Dictionary<string, object>();
for (var i = 0; i < attrs.Length; i = i + 2)
res.Add(attrs[i].ToString(), attrs[i + 1]);
return res;
}
public static RouteValueDictionary Route(params object[] attrs)
{
return new RouteValueDictionary(Dic.New(attrs));
}
}
You can use it like this:
In Mvc:
#Html.TextBoxFor(x=>x.Id,new{#data_val_number="10"});
In Html:
<input type="text" name="Id" data_val_number="10"/>
i'm just starting to use asp.net mvc..
since i'm not familiar with it, i just want to ask a question about actionlink html helper..
i have this code in my index.aspx home view..
<% Dim _news As datatable = ViewData.Model%>
<% For count As Integer = 0 To _news.Rows.Count - 1%>
<% Dim id As Integer = _news.Rows(count).Item("IDnews")%>
<%=_news.Rows(count).Item("newsTitle")%>
<p>
<%=_news.Rows(count).Item("newsContent")%><br />
<%=Html.ActionLink("Read More..", "NewsPublic", "Administration", New With {id})%>
</p>
<%Next%>
if i click the actionlink, i was expecting it will render me to this url:
/Administration/NewsPublic/7
but rather it gives me this url :
/Home/NewsPublic?Length=14
does actionlink pass id in the same controller only?
thank you in advance!
To render link to /Administration/NewsPublic/7 you should use
<%=Html.ActionLink("Read More..", "NewsPublic", "Administration",
New With {.id = 7}, Nothing)%>
Fifth parameter makes compiler to choose
ActionLink(string linkText, string actionName, string controllerName,
object routeValues, object htmlAttributes)
extension method overload instead of
ActionLink(string linkText, string actionName, object routeValues,
object htmlAttributes)
And don't forget to add parameter assignment
New With {.id = 7}
instead of
New With {.id}
By default, Html.ActionLink will use the current controller. But there are about a dozen overloads of ActionLink(), and there are multiple versions of it that will accept a controller parameter. Try:
Html.ActionLink("Read More...",
"NewsPublic",
"Administration",
New With { .id = id },
null)