I'm fairly new to MVC 3 and am using the Razor view engine. I'm using the Html.Hidden extension method to output input elements of type hidden. What I woudl also like to do is add a custom attribute to hold a dynamic value. I was under the impression in HTML5 wee could write custom html element attributes that are prefixed with 'data-'. I'm trying to do something like below;
#Html.Hidden("hdnID", mymodel.somevalue, new { data-uniqueid = mymodel.somevalue })
hoping to render;
<input type="hidden" value="mymodel.somevalue" data-uniqueid="mymodel.somevalue"/>
The htmlAttributes part (new { data-uniqueid = mymodel.somevalue }) is giving the error,
"Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access".
Can I add user-defined attribute to html elements using the HtmlHelper classes?
Regards,
Use:
#Html.Hidden("hdnID", mymodel.somevalue, new { #data_uniqueid = mymodel.somevalue })
The underscore gets automatically converted to a dash.
Doh! I was being silly. You can't have '-' in the anon type declaration:
data-uniqueid = ...it must be
datauniqueid = ....
In that case, your best best is to write out the hidden input by hand:
<input type="hidden" value="#mymodel.somevalue" data-uniqueid="#mymodel.somevalue"/>
You can step around the member validation by constructing a dictionary object. As follows:
#Html.TextBoxFor(model => model.Phone, new Dictionary<string, object>
{
{
"data-call-results-target", "#search-results-area"
},
{
"data-action-path", "/Controler/Method"
}
})
Related
How does one add a jQuery Mobile transition to an HTML POST rendered with the ASP.Net MVC Html.BeginForm helper?
The transition requires an HTML attribute data-transition be added (I think to the form tag, but the docs are unclear on that point, providing only a hyperlink example).
I'm trying to use the BeginForm overload to add attributes to the rendered form tag. When using the new { ... } syntax to declare an anonymous class representing the HTML attributes, I get an error if an attribute name has a dash in it.
using (Html.BeginForm("Login", "Account", FormMethod.Post,
new { data-transition="pop" }))
Error: Invalid anonymous type member declarator
This, in spite of the fact that the MSDN documentation shows an attribute with a dash in the name
new { id = "text1", accept-charset="iso-8859-1" }
Create a dictionary:
using (Html.BeginForm("Login", "Account", FormMethod.Post,
new Dictionary<string, object>{{ "data-transition", "pop" }} ))
If you prefer to use an anonymous object to specify your attributes then you could do the following
using (Html.BeginForm("Login", "Account", FormMethod.Post, new { data_transition = "pop" } ))
In short you replace the hypen with an underscore
I have a property of type "float" in my ViewModel. It's being displayed as a TextBox with a default value of "0".
I added an "EditorTemplates" folder inside the "Shared" folder & created a new "Float.cshtml" file with the following content:
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue == 0 ? "" : ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line" })
However, still when I run the application, float fields are still being displayed with a default value of 0.
Thanks
UPDATE
I am just trying to see how ASP.NET reacts to custom templates, till now, the engine is not processing my custom template, something similar to:
LatLng.cshtml
#model float
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line "}) Latitude
On the ViewModel,
[UIHint("LatLng")]
public float? Latitude { get; set; }
On the View,
#Html.EditorFor(model => model.Latitude)
Nothing is changing, default template is being used.
Float is not actually a .NET type, it's a C# type. Float maps to System.Single, so you need to create a Single.cshtml and not a Float.cshtml.
You can also get around this by specifying a UIHint attribute on the model data, or by specifying the template to use in your Editor or EditorFor methods.
An easy workaround is if you just set ViewData.TemplateInfo.FormattedModelValue to return a string in the model, so you don't have to do that weird logic on the view. If you need it to post back a new value (for editing purposes), you just have to add some logic in the controller to turn the string back into a float.
In my view, I have a statement like this:
<li>#Html.EditorFor(model => model.SomeEnum, "Enum", new { ShowDefaultText = false })</li>
I have a Enum (SomeEnum) and I have a editor for template for enums. In my editor for template I'm trying to check the ViewData object for the anonymous class I passed it. According to he Html.EditorFor documentation, the third parameter is additional view data that will be with the ViewDataDictionary object.
However, in my template when looking at the ViewData class, I don't see the anonymous class/property in it. Am I doing something wrong, am I looking at the wrong object in my editor for template?
Use the following ViewData syntax
#{
var boolValue = Convert.ToBoolean(ViewData["ShowDefaultText"]);
}
#if (!boolValue)
{
...............
}
If I am passing HtmlAttributes into a template, like this:
#Html.DisplayFor(m => m.FirstName, new { htmlAttributes = new { #class = "orangetxt strongtxt" } })
In my template, how would I inject these into my HTML:
<span #ViewData["htmlAttributes"]>#Model</span>
This almost works, but it does some pretty weird stuff, so I'm assuming this isn't the way to go.
I realize I can accomplish this with an HtmlHelper extension method to render the full HTML element (span, in this case) and pass in the attributes that way, but is there a way to just render attributes straight into an HTML element, like the above example?
The below extension method will allow me to convert HtmlAttributes to a string:
public static MvcHtmlString RenderHtmlAttributes<TModel>(
this HtmlHelper<TModel> htmlHelper, object htmlAttributes)
{
var attrbituesDictionary = new RouteValueDictionary(htmlAttributes);
return MvcHtmlString.Create(String.Join(" ",
attrbituesDictionary.Select(
item => String.Format("{0}=\"{1}\"", item.Key,
htmlHelper.Encode(item.Value)))));
}
Then, to render them within the tag, I can just do this:
<span #Html.RenderHtmlAttributes(ViewData["htmlAttributes"])>#Model</span>
Jerad Rose's answer is good, but I ran into couple of issues with it:
It does not not convert underscores to dashes in attribute names
It does not handle no-value attributes gracefully
To address first issue, use HtmlHelper.AnonymousObjectToHtmlAttributes.
Below is my modification of Jerad's method:
public static MvcHtmlString RenderHtmlAttributes(this HtmlHelper helper, object htmlAttributes)
{
if (htmlAttributes == null) return new MvcHtmlString(String.Empty);
var attrbituesDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
return new MvcHtmlString(String.Join(" ", attrbituesDictionary.Select(item => string.IsNullOrEmpty((string)item.Value) ? String.Format("{0}", item.Key) : String.Format("{0}=\"{1}\"", item.Key, helper.Encode(item.Value)))));
}
Try this instead,
#Html.DisplayFor(m => m.FirstName,
new { htmlAttributes = "class = orangetxt strongtxt"})
This will render a string, whereas your version did do weird stuff, rendered { } as part of the output.
DisplayFor() is used to render the template that matches the property type.
Display templates are .cshtml files inside /DisplayTemplates folder which in turn is inside a view folder (i.e. any folder from Home, Shared or even a specific controller).
An example.
If you've a String.cshtml template like this inside /Views/Shared:
#model String
#if (string.IsNullOrEmpty(Model)) {
<span>(no string)</span>
}
else {
<span>#Model</span>
}
Every time you call DisplayFor() for a string property:
DisplayFor(model => model.MyStringProperty);
It renders the template accordingly to the string's value. You can be more specific and put /DisplayTemplates inside a specific View folder and them only calls from those views are affected by the template.
In your case you can be even more specific and call DisplayFor() with a particular template.
Suppose you've a template for a particular property, called MyPropertyTemplate.cshtml. You would call DisplayFor() like this:
DisplayFor(model => model.MyProperty, "MyPropertyTemplate");
And them, inside that template you can have whatever HTML attributes you want.
#model MyProperty
<span class="orangetxt strongtxt">#MyProperty.ToString()</span>
PS: When it doesn't find a template I guess it only calls model.Property.ToString() without additional html.
FYI: EditorFor(), for example, works in a similar way but it uses /EditorTemplates folder.
This code was converted from some ASP.Net MVC 2 code in this tutorial:
MVC 2 Editor Template with DateTime
It is a custom EditorTemplate for DateTime fields stored as 'EditorTemplates/DateTime.cshtml'.
#Model DateTime?
#Html.TextBox("", (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty), new { #class = "datePicker" })
However I get the following error when using #Html.EditorFor(model => model.NewAbsence.StartDate):
CS1973: 'System.Web.Mvc.HtmlHelper' has no applicable method named 'TextBox' 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.
I've seen some similar posts on here which mention casting the parameter of the EditorFor method, however I cannot seem to get this to work in my example.
Could someone please point out what I will need to change in my code. Thanks.
Actually it's #model with lowercase m:
#model DateTime?
^
instead of:
#Model DateTime?
So to sort of summarize what people are saying, and make it a bit more generic. If your view is declaring that it accepts dynamic models:
#model dynamic
Then things like extension methods will not be able to infer the types of arguments passed to them. Here are two examples (using Razor because it's awesome):
#Html.TextBox("myTextBoxName", Model.MyTextBoxValue)
#Html.DropDownList("myDropDownName", Model.MySelectList))
In these cases, the engine doesn't know what types Model.MyTextBoxValue or Model.MySelectList are, therefore it can't figure out what overloads of the extension methods to compile. So you just help it along with some strong typing:
#Html.TextBox("myTextBoxName", (string)Model.MyTextBoxValue)
#Html.DropDownList("myDropDownName", (SelectList)Model.MySelectList))
By the way, just to stop people from potentially pulling out their hair, that SelectList has to be properly instantiated with something like:
var items = List<SelectListItem>();
...
new SelectList(items, "Value", "Text");
As a temporary work around I am using:
<div class="editor-field date-field">
#Html.EditorFor(model => model.NewAbsence.StartDate)
#Html.ValidationMessageFor(model => model.NewAbsence.StartDate)
</div>
Then using the jQuery selector:
$(".date-field > input").datepicker({
showOn: "button",
buttonImage: "*pathtoimage*"
});
To apply the date picker to the input tags within the 'date-field' div. However this still doesn't format the date value how I want it to display initially, and cuts out the editor template entirely.
The error message comes from your textbox statement. In a template, this becomes a dynamic expression, and .Net doesn't know how to type the Model properties.
#Html.TextBox("", (string)(Model==null ? Model.Value.ToShortDateString() : string.Empty), new { style = "width: 10em;", #class="datefield" })
Explicitly cast your date value as string, and the dynamic expression has the information it needs. I also had a problem with the .HasValue property, but that wasn't the point of your question.