I am using an MVC3 WebGrid, and want to add custom attributes of the form "data-xxx" to the <table> element rendered by WebGrid.GetHtml().
I tried to do this as follows:
grid.GetHtml(...
htmlAttributes: new { data_xxx = "value" }
);
However this renders as:
<table ... data_xxx="value">
instead of the expected:
<table ... data-xxx="value">
I.e. unlike other MVC helpers, it doesn't replace the underscore in the property name with a hyphen.
After a bit of spelunking with Reflector it seems that:
WebGrid.GetHtml() calls System.Web.WebPages.Html.ObjectToDictionary to convert the anonymous attributes object to a dictionary. Unlike the method System.Web.Mvc.HtmlHelper.AnonymousObjectToHtmlAttributes used by other helpers, this doesn't replace "_" by "-".
Unlike most other MVC helpers, WebGrid.GetHtml() does not have an overload that takes an IDictionary<string, object>, so I can't work around the bug by specifying the attributes in this way.
This looks like a bug to me, and I've submitted it to Connect:
http://connect.microsoft.com/VisualStudio/feedback/details/767456/webgrid-gethtml-helper-does-not-allow-data-attributes-to-be-set
Any suggestions for a workaround? Or is it fixed in MVC4?
Thanks for reporting this issue:
This was fixed yesterday in CodePlex by resolving issues 610 & 575.
https://aspnetwebstack.codeplex.com/workitem/610
https://aspnetwebstack.codeplex.com/workitem/575
You can try out the nightly bits.
Related
Using ASP.NET Core 3 Razor Pages, suppose you would like to set up pages with the following URL structure:
http://contoso.com/course/4231/details
http://contoso.com/course/4231/students
where 4231 is the course ID. This can be achieved by using the #page directive:
#page "/course/{courseId}/details"
Now suppose you want to create links between the /details and the /students pages. You then want the courseId parameter to be added to every link, which can be accomplished using the anchor tag helper as follows:
<a asp-page="./students" asp-route-courseid="#RouteData.Values["courseId"]>Students</a>
As far as I've understood it, in ASP.NET Core 2.1 and earlier versions it was not necessary to add the asp-route-* attribute for this purpose, because route values would 'propagate' to other pages, being added automatically to any anchor tag unless manually overridden ("ambient route values", as it was called). This was apparently removed in version 2.2, but I'm not quite sure why.
Having to always remember to manually propagate route values using the above scheme seems like a very error-prone workflow, and could get out of hand quick if you have many route values that always needs to be added to anchor tags.
Is it possible to manually enable ambient route values in later versions of ASP.NET Core, at the very least for individual parameters? Is there any reason not to do this?
Essentially, I'd like for the anchor tag helpers to be relative, so that just linking to ./students from /course/4231/details automatically resolves to /course/4231/details.
Since I managed to find a solution for this, I'm going to answer my own question.
As mentioned in this GitHub issue (credit to Oliver Weichhold), you can override the default anchor tag helper (and form action tag helper) to achieve the desired behaviour of having route values automatically added to links.
I ended up implementing derived versions of the tag helpers that will automatically add route values if they are part of the route template, because that's the behaviour I was looking for. To keep it short, the Process method of the tag helpers looks like this:
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var routeTemplate = ViewContext.ActionDescriptor.AttributeRouteInfo.Template;
Regex regex = new Regex(#"\{(\w+)(:.+)?\??\}");
var matches = regex.Matches(routeTemplate);
if (matches.Count > 0)
foreach (var routeValueKvp in ViewContext.HttpContext.Request.RouteValues)
if (matches.Any(x => x.Groups[1].Value == routeValueKvp.Key))
RouteValues[routeValueKvp.Key] = routeValueKvp.Value.ToString();
base.Process(context, output);
}
This solution works, but it's not optimal. One of the reasons is that the custom tag helpers needs to be decorated with the HtmlTargetElement attributes manually, which means that if the base versions of the tag helpers gets extended with new attributes in future releases of the framework, the custom versions will need to be updated manually.
IMO, it is expected. When you redirect to the same action/page, the current ambient route values are being reused.
<a asp-page="details">Studentdetails</a>
It generates url: /course/4231/details.
When you redirect to a different action/page, the ambient values are correctly ignored.
<a asp-page="./students">Students</a>
it will not generate correct url.
Refer to https://github.com/dotnet/aspnetcore/issues/3746
.Net 4.0 is encoding single quotes when I am using Attributes.Add to add client side events to my asp.net objects. In the previous versions this didn't happen.
for example :
<asp:Image runat="server" ID="imgTest" ImageUrl="~/DateControl/cal.gif" />
imgTest.Attributes.Add("onmouseover", "alert('Hello')");
When I view the client side output, I am getting
<img id="ctl00_MainContent_calFromTimeStamp1_imgTest" onmouseover="alert('Hello')" src="../DateControl/cal.gif" style="border-width:0px;" />
I found a workaround by creating a custom encoder : creating custom encoding routines but I don't want to stop the encoding for the whole website just because of this issue. Anybody got a workaround or an idea of how to fix this?
According to Microsoft you should not be adding JavaScript to HTML attributes using WebControl.Attributes.Add(), exactly because it will encode the attribute value:
You cannot add client-side script to a WebControl instance using the
Attributes collection. To add client-side script, use the ClientScript
property on the Page control.
Source
The advice is to use the Page.ClientScript.RegisterExpandoAttribute(string controlId, string attributeName, string attributeValue, bool encode) method. In your case it would look like this:
Page.ClientScript.RegisterExpandoAttribute(
imgTest.ClientID,
"onmouseover",
"alert('Hello')",
false /* Do not encode */
);
This will result in a piece of JavaScript in your page that sets the attribute client-side.
The best way to set event attributes in .NET is to call a single function :
imgTest.Attributes("message") = "Hello";
imgTest.Attributes("onmouseover") = "showMessage(this);"
And on your page, or registered script :
function showMessage(ctrl)
{
alert(ctrl.getAttribute('message'));
}
imgTest.Attributes.Add("onmouseover", "alert(\'Hello\')");
Thanks to Franzo's link, where the following answer is copied and pasted:
You can turn off attribute encoding by creating a class like this:
public class HtmlAttributeEncodingNot : System.Web.Util.HttpEncoder
{
protected override void HtmlAttributeEncode(string value, System.IO.TextWriter output)
{
output.Write(value);
}
}
and adding this to web.config under :
<httpRuntime encoderType="HtmlAttributeEncodingNot"/>
This gives me the control I need.
However, now we must worry that new controls may depend on the new
standard 4.0 behaviour and not encode single quotes, so it's still
imperfect, nay, worse than imperfect: security is even worse, because
we don't know what is going on where, so it's not a great workaround
really.
I think only Microsoft can fix this properly. Others have suggested
the need for an HtmlAttributeString class here: link If there
were such a class and Attributes.Add could take an object like this
for its value parameter then we would have the control that we need
again.
It's not recommended to turn off attribute encoding. If you try to prevent encoding by default, there are many strange behaviors occurring with your code in future and you have to pay price for bad practices.
.NET is always encoding any attributes to stop from injecting malicious script. So you should go by this default practice to protect your program.
You can use an escape character before any quote character :
Source :
this.Attributes.Add("onmouseover", string.Format("$(this).attr(\'src\',\'{0}\')",this.Page.ClientScript.GetWebResourceUrl(typeof(SwapImage), urlenabledkey)));
Render :
onmouseover="$(this).attr('src','/WebResource.axd?d=kHY3FE9nMsUOvDU-pPthg4KQvVrnXlcASyA7dFf6L
I'm struggling with the following problem and I can't find an acceptable way to solve it.
My challenge: write out HTML comments just before the actual property value in a Razor view.
This is my (simplyfied) Viewmodel:
public class Article
{
public string Title {get;set;}
}
To write out this title I simply do this in my Razor view:
<h2>#Model.Title</h2>
Now I want to write out a html comment just before the actual title so the generated HTML looks like this (simplyfied):
<h2><!-- some parameters for a 3th party system --> This is my title</h2>
The HTML comment comes from an Attribute I applied to the 'Title' attribute. It's value is generated, so the attribute-value is added at runtime using the TypeDescriptor from the .NET framework.
Now I know I could achieve this by simply writing out all my properties using an HTML helper. Like this: #MyHelper.Write(m => m.Title)
But since potentially ALL my properties need this HTML comment I want to avoid the use of an HTML helper since it clutters the View and doesn't make the view look nice and (more) readable.
This is what I have tried:
Created a custom Razor base page (Inheriting from WebViewPage<TModel>). And overwriting it's 'Write' method.
This kind of works but the BIGGEST problem here is that I don't know which property is been written out at that moment. There is no way of getting the current property name in the 'Write' method. So now I dynamically search my Model to find a property with the value that's been written out and prepend the HTML comment from the attribute.
My question: is there another approach to accomplish what I want. As sais before: I want to avoid using an HTML helper to write out all my properties. (Think about loops, etc. It's just not nice).
Also, adding this HTML comment in my Controller is no option since:
it's not part of the actual value. Is a sort of metadata.
The HTML comment should be added to int's, double's and DateTime's. There is no way to adjust a double property to include a string. (Image a List<DateTime>. All date's need this HTML comment)
the HTML comment should be added based on a web.config setting. Yes or No. (The actual HTML comment is different for each value of a property)
I realize this question is rather long. Sorry for that. Any thoughts are appreciated.
You can use the existing #Html.Raw(Model.Title)
Alternatively you can use a display templates. Add a UIHintAttribute to the properties you wish to behave this way.
public class MyModel
{
[UIHint("Raw")]
public string MyString { get; set; }
}
Create a new display template called Raw.cshtml that accepts model of type string:
#model string
#Html.Raw(model)
Then in your view you can use:
#Html.DisplayFor(m => m.MyString)
This still requires that you use a helper (DisplayFor). This is a recommended practice that allows you to easily change the behavior of one or many fields with minimal code changes.
I am using a Html.TextBoxFor helper in an MVC4 project. Our web designer used a custom property of "error-type" for his box that jquery/javascript looks at to determine how an error should be rendered for that textbox.
I tried doing something like:
#Html.TextBoxFor(m => m.SomeValue, new { error-type="blue" });
But C# doesn't like the property name of "error-type".
Is there a way to be able to use the custom property when rendering it through Razor?
Use an underscore; the helper will convert attribute names containing underscores to dashes. FYI, I believe this will only work with MVC-3 or later.
An alternate route is to use the overload that accepts an IDictionary<string, object> of HTML attributes. That will be both forwards and backwards compatible.
#Html.TextBoxFor(m => m.SomeValue, new Dictionary<string, object>{{"error-type", "blue"}})
Just wondering how do I mimic the following using attributes...
<%= Html.EditorFor(x => x.SportProgramIdList, "FormMultiSelectDropDownList", "SportProgramIds")%>
I know I can specify the template by using [UIHint("FormMultiSelectDropDownList")] but I am left with the problem with how to set the name...
Cheers
Anthony
I Guess you'll have to create your own CustomAttribute UINameAttribute.
You could use the ModelMetadata to keep your attribute and then I'm not sure what would be the best way to get it, i guess you would have to overide the HtmlHelper.EditorFor Extension without the parameter and pass your attribute to the next.
I'm too lazy and to tired to try for a more complete answer.
look at :
Why You Don't Need ModelMetadata.Attributes
ASP.NET MVC 2 Templates, Part 2: ModelMetadata
Use the objectAttributes argument
<%= Html.EditorFor(x => x.SportProgramIdList, "FormMultiSelectDropDownList", new { id="SportProgramIds" }) %>>
you can use this to set any of the html input attributes