114

Asp.Net MVC 2.0 preview builds provide helpers like

Html.EditorFor(c => c.propertyname)

If the property name is string, the above code renders a texbox.

What if I want to pass in MaxLength and Size properties to the text box or my own css class property?

Do I need to create one template for each size and length combinations in my application? If so, that doesn't make the default templates that usable.

WEFX
  • 7,709
  • 8
  • 59
  • 93
chandmk
  • 3,456
  • 3
  • 19
  • 26

20 Answers20

92

In MVC3, you can set width as follows:

@Html.TextBoxFor(c => c.PropertyName, new { style = "width: 500px;" })
WEFX
  • 7,709
  • 8
  • 59
  • 93
61

I solved this by creating an EditorTemplate named String.ascx in my /Views/Shared/EditorTemplates folder:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<% int size = 10;
   int maxLength = 100;
   if (ViewData["size"] != null)
   {
       size = (int)ViewData["size"];
   }
   if (ViewData["maxLength"] != null)
   {
       maxLength = (int)ViewData["maxLength"];
   }
%>
<%= Html.TextBox("", Model, new { Size=size, MaxLength=maxLength }) %>

In my view, I use

<%= Html.EditorFor(model => model.SomeStringToBeEdited, new { size = 15, maxLength = 10 }) %>

Works like a charm for me!

tjeerdhans
  • 860
  • 7
  • 11
  • 1
    brilliant - I had a datepicker DateTime drop down that I'd already templated, but passing extra attributes to it was proving painful - this solved it for me, thanks! – Terry_Brown Oct 15 '10 at 10:02
  • EditorFor with maxlength doesn't work for me (TextBoxFor on the other does) – Drejc Jan 25 '13 at 10:18
  • @tjeerdhans, I used this code to customize my css. It works but unfortunately it **replaces** the original css values. How can I make it **append** to the original css instead? – Rosdi Kasim Feb 25 '13 at 03:04
33

None of the answers in this or any other thread on setting HTML attributes for @Html.EditorFor were much help to me. However, I did find a great answer at

Styling an @Html.EditorFor helper

I used the same approach and it worked beautifully without writing a lot of extra code. Note that the id attribute of the html output of Html.EditorFor is set. The view code

<style type="text/css">
#dob
{
   width:6em;
}
</style>

@using (Html.BeginForm())
{
   Enter date: 
   @Html.EditorFor(m => m.DateOfBirth, null, "dob", null)
}

The model property with data annotation and date formatting as "dd MMM yyyy"

[Required(ErrorMessage= "Date of birth is required")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd MMM yyyy}")]
public DateTime DateOfBirth { get; set; }

Worked like a charm without writing a whole lot of extra code. This answer uses ASP.NET MVC 3 Razor C#.

wayne.blackmon
  • 694
  • 13
  • 22
  • 1
    Worked really well for me too in MVC4. – atconway Aug 16 '12 at 14:30
  • 1
    The formatting worked but now null is returned for the data effectively disabling the box. – Joe Nov 09 '12 at 05:15
  • 2
    I don't know what it is with MVC people, it's like nobody has every written a website that works with multiple languages and formats for numbers. – adudley Nov 09 '12 at 15:16
  • Very good. This is simple, it works, and it actually answer's the OP's question, instead of providing alternatives. Using MVC4. – draconis Dec 16 '14 at 11:51
  • Warning: DateTimes appear to render as type="datetime", which is wrong for a birthdate, and some browsers (like Firefox mobile) actually correctly support that input type, including a time component that probably won't pass validation and is difficult or impossible for the user to remove. – brianary Nov 25 '15 at 19:56
25

May want to look at Kiran Chand's Blog post, he uses custom metadata on the view model such as:

[HtmlProperties(Size = 5, MaxLength = 10)]
public string Title { get; set; }

This is combined with custom templates that make use of the metadata. A clean and simple approach in my opinion but I would like to see this common use case built-in to mvc.

tj.
  • 259
  • 2
  • 2
  • 71
    Are you kidding? This is far too much overkill for such a simple thing. More and more I am going back to pure HTML and forgetting the MVC bloated helper methods. 5 minutes ago I had a simple TextboxFor which successfully launched a jquery datepicker. Now just because I wanted to change how it formats the pre-loaded date value in, I had to change it to an EditorFor. But now suddenly I can't specify my own HTML attributes any more without writing these overly bloated custom extension methods and helpers. What a joke. There has to be a simpler way, you mad professors don't know when to stop. – Aaron Feb 17 '11 at 00:09
  • 1
    I think you are mixing up contexts. How can you have two different EditorFor calls with each one getting its own size and maxlength values with the link you provided? If you don't want to use EditorFor, you always can use TextBox helper or simple html. But that is not the question! – chandmk Feb 24 '11 at 00:11
  • @Aaron as of latest MVC, you can specify additional html attributes with `EditorFor` by passing it as: `new { htmlAttributes: { @class = "yourclass" } }` – JoeBrockhaus Dec 19 '14 at 17:28
18

I'm surprised no one mentioned passing it in "additionalViewData" and reading it on the other side.

View (with line breaks, for clarity):

<%= Html.EditorFor(c => c.propertyname, new
    {
        htmlAttributes = new
        {
            @class = "myClass"
        }
    }
)%>

Editor template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>

<%= Html.TextBox("", Model, ViewData["htmlAttributes"])) %>
Ishmael Smyrnow
  • 904
  • 1
  • 8
  • 25
  • Building on this I created a helper that takes into account merging of attributes - http://stackoverflow.com/a/11498094/79842 – Colin Bowern Jul 16 '12 at 04:30
6

I think using CSS is the way to go. I wish I could do more with .NET coding, like in XAML, but in the browser CSS is king.

Site.css

#account-note-input { 
  width:1000px; 
  height:100px; 
} 

.cshtml

<div class="editor-label"> 
  @Html.LabelFor(model => model.Note) 
</div> 
<div class="editor-field"> 
  @Html.EditorFor(model => model.Note, null, "account-note-input", null) 
  @Html.ValidationMessageFor(model => model.Note) 
</div>

Joe

Joe Kahl
  • 741
  • 1
  • 6
  • 8
  • This works very well for making control specific CSS changes when using the `EditorFor` template. I am using MVC4 and this worked great. – atconway Aug 16 '12 at 14:29
6

The problem is, your template can contain several HTML elements, so MVC won't know to which one to apply your size/class. You'll have to define it yourself.

Make your template derive from your own class called TextBoxViewModel:

public class TextBoxViewModel
{
  public string Value { get; set; }
  IDictionary<string, object> moreAttributes;
  public TextBoxViewModel(string value, IDictionary<string, object> moreAttributes)
  {
    // set class properties here
  }
  public string GetAttributesString()
  {
     return string.Join(" ", moreAttributes.Select(x => x.Key + "='" + x.Value + "'").ToArray()); // don't forget to encode
  }

}

In the template you can do this:

<input value="<%= Model.Value %>" <%= Model.GetAttributesString() %> />

In your view you do:

<%= Html.EditorFor(x => x.StringValue) %>
or
<%= Html.EditorFor(x => new TextBoxViewModel(x.StringValue, new IDictionary<string, object> { {'class', 'myclass'}, {'size', 15}}) %>

The first form will render default template for string. The second form will render the custom template.

Alternative syntax use fluent interface:

public class TextBoxViewModel
{
  public string Value { get; set; }
  IDictionary<string, object> moreAttributes;
  public TextBoxViewModel(string value, IDictionary<string, object> moreAttributes)
  {
    // set class properties here
    moreAttributes = new Dictionary<string, object>();
  }

  public TextBoxViewModel Attr(string name, object value)
  {
     moreAttributes[name] = value;
     return this;
  }

}

   // and in the view
   <%= Html.EditorFor(x => new TextBoxViewModel(x.StringValue).Attr("class", "myclass").Attr("size", 15) %>

Notice that instead of doing this in the view, you may also do this in controller, or much better in the ViewModel:

public ActionResult Action()
{
  // now you can Html.EditorFor(x => x.StringValue) and it will pick attributes
  return View(new { StringValue = new TextBoxViewModel(x.StringValue).Attr("class", "myclass").Attr("size", 15) });
}

Also notice that you can make base TemplateViewModel class - a common ground for all your view templates - which will contain basic support for attributes/etc.

But in general I think MVC v2 needs a better solution. It's still Beta - go ask for it ;-)

queen3
  • 14,883
  • 8
  • 54
  • 114
  • I think a better way of dealing with this is as I mentioned here http://stackoverflow.com/questions/1647609/asp-net-mvc-v2-styling-templates... In short doing something similar to the way that WPF handles the problem... Arbitrary styles elements (on in this case attributes) get passed to the template and the template decides which internal element it will apply the style to... – vdhant Oct 30 '09 at 23:06
6

As at MVC 5, if you wish to add any attributes you can simply do

 @Html.EditorFor(m => m.Name, new { htmlAttributes = new { @required = "true", @anotherAttribute = "whatever" } })

Information found from this blog

Jay
  • 974
  • 1
  • 16
  • 28
3

You can define attributes for your properties.

[StringLength(100)]
public string Body { get; set; }

This is known as System.ComponentModel.DataAnnotations. If you can't find the ValidationAttribute that you need you can allways define custom attributes.

Best Regards, Carlos

3

This may not be the slickest solution, but it is straightforward. You can write an extension to the HtmlHelper.EditorFor class. In that extension, you can supply an options parameter that will write the options to the ViewData for the helper. Here's some code:

First, the extension method:

public static MvcHtmlString EditorFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, TemplateOptions options)
{
    return helper.EditorFor(expression, options.TemplateName, new
    {
        cssClass = options.CssClass
    });
}

Next, the options object:

public class TemplateOptions
{
    public string TemplateName { get; set; }
    public string CssClass { get; set; }
    // other properties for info you'd like to pass to your templates,
    // and by using an options object, you avoid method overload bloat.
}

And finally, here's the line from the String.ascx template:

<%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = ViewData["cssClass"] ?? "" }) %>

Frankly, I think this is straightforward and clear to the poor soul who has to maintain your code down the road. And it is easy to extend for various other bits of info you'd like to pass to your templates. It's working well so far for me in a project where I'm trying to wrap as much as I can in a set of template to help standardize the surrounding html, a la http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-5-master-page-templates.html.

spot
  • 2,413
  • 2
  • 18
  • 16
3

I wrote a blog entry to answer my own question

Adding html attributes support for Templates - ASP.Net MVC 2.0 Beta

chandmk
  • 3,456
  • 3
  • 19
  • 26
  • The idea of putting HTML formatting properties on the (view) model is against the idea of MVC to separate view and logic! What if the model would need to different representations in HTML? – saintedlama Jan 08 '12 at 08:05
  • 2
    @Saintedlama: It's perfectly fine to put data annotations on the *viewmodel*, because viewmodel's only purpose is to provide a structured data model for the *view*. Not to be confused with *model*, which in MVC terminology usually represents a DB entity, which should not have any view-related properties. Having said that, it is unfortunate that his viewmodel annotations start with HTMLxxx because that implies HTML as a presentation layer, but a line has to be drawn somewhere. :) Besides, if he reuses the viewmodel for say silverlight app, he can easily add SL attributes to his viewmodel. – Boris B. Feb 07 '12 at 10:13
3

I don't know why it does not work for Html.EditorFor but I tried TextBoxFor and it worked for me.

@Html.TextBoxFor(m => m.Name, new { Class = "className", Size = "40"})

...and also validation works.

Piotr Czyż
  • 2,674
  • 2
  • 18
  • 14
2

Because the question is for EditorFor not TextBoxFor WEFX's suggestion doesn't work.

For changing individual input boxes, you can process the output of the EditorFor method:

<%: new HtmlString(Html.EditorFor(m=>m.propertyname).ToString().Replace("class=\"text-box single-line\"", "class=\"text-box single-line my500pxWideClass\"")) %>

It is also possible to change ALL your EditorFors as it turns out MVC sets the class of EditorFor text boxes with .text-box, therefore you can just override this style, in your stylesheet or on the page.

.text-box {
    width: 80em;
}

Additionally, you could set the style for

input[type="text"] {
    width: 200px;
}
  • this overrides .text-box and will change all input text boxes, EditorFor or otherwise.
stuartdotnet
  • 2,600
  • 3
  • 31
  • 35
  • Not all EditorFor() text boxes are input type="text". DateTime properties seem to render as type="datetime". – brianary Nov 25 '15 at 19:59
2

I also had issue with setting the width of TextBox in MVC3, while setting the Clsss attribute worked for TextArea control but not for TextBoxFor control or EditorFor control:

I tried following & that worked for me:

@Html.TextBoxFor(model => model.Title, new { Class = "textBox", style = "width:90%;" })

also in this case Validations are working perfectly.

Ashish
  • 41
  • 1
2

One way you could get round it is by having delegates on the view model to handle printing out special rendering like this. I've done this for a paging class, I expose a public property on the model Func<int, string> RenderUrl to deal with it.

So define how the custom bit will be written:

Model.Paging.RenderUrl = (page) => { return string.Concat(@"/foo/", page); };

Output the view for the Paging class:

@Html.DisplayFor(m => m.Paging)

...and for the actual Paging view:

@model Paging
@if (Model.Pages > 1)
{
    <ul class="paging">
    @for (int page = 1; page <= Model.Pages; page++)
    {
        <li><a href="@Model.RenderUrl(page)">@page</a></li>
    }
    </ul>
}

It could be seen as over-complicating matters but I use these pagers everywhere and couldn't stand seeing the same boilerplate code to get them rendered.

Phil Cooper
  • 2,944
  • 37
  • 59
2

In my practice I found that it is best to use EditorTemplates with only one HtmlHelper in it - TextBox that is in most cases. If I want a template for more complex html structure, I'll write a separate HtmlHelper.

Given that we can stick the whole ViewData object in place of htmlAttributes of the TextBox. In addition we can write some customization code for some of the properties of the ViewData if they need special treatment:

@model DateTime?
@*
    1) applies class datepicker to the input;
    2) applies additionalViewData object to the attributes of the input
    3) applies property "format" to the format of the input date.
*@
@{
    if (ViewData["class"] != null) { ViewData["class"] += " datepicker"; }
    else { ViewData["class"] = " datepicker"; }
    string format = "MM/dd/yyyy";
    if (ViewData["format"] != null)
    {
        format = ViewData["format"].ToString();
        ViewData.Remove("format");
    }
}

@Html.TextBox("", (Model.HasValue ? Model.Value.ToString(format) : string.Empty), ViewData)

Below are the examples of the syntax in the view and the outputted html:

@Html.EditorFor(m => m.Date)
<input class="datepicker" data-val="true" data-val-required="&amp;#39;Date&amp;#39; must not be empty." id="Date" name="Date" type="text" value="01/08/2012">
@Html.EditorFor(m => m.Date, new { @class = "myClass", @format = "M/dd" })
<input class="myClass datepicker" data-val="true" data-val-required="&amp;#39;Date&amp;#39; must not be empty." id="Date" name="Date" type="text" value="1/08">
Dmitry Efimenko
  • 10,224
  • 7
  • 60
  • 73
1

UPDATE: hm, obviously this won't work because model is passed by value so attributes are not preserved; but I leave this answer as an idea.

Another solution, I think, would be to add your own TextBox/etc helpers, that will check for your own attributes on model.

public class ViewModel
{
  [MyAddAttribute("class", "myclass")]
  public string StringValue { get; set; }
}

public class MyExtensions
{
  public static IDictionary<string, object> GetMyAttributes(object model)
  {
     // kind of prototype code...
     return model.GetType().GetCustomAttributes(typeof(MyAddAttribute)).OfType<MyAddAttribute>().ToDictionary(
          x => x.Name, x => x.Value);
  }
}

<!-- in the template -->
<%= Html.TextBox("Name", Model, MyExtensions.GetMyAttributes(Model)) %>

This one is easier but not as convinient/flexible.

queen3
  • 14,883
  • 8
  • 54
  • 114
1

This is the cleanest and most elegant/simple way to get a solution here.

Brilliant blog post and no messy overkill in writing custom extension/helper methods like a mad professor.

http://geekswithblogs.net/michelotti/archive/2010/02/05/mvc-2-editor-template-with-datetime.aspx

Aaron
  • 1,783
  • 3
  • 19
  • 49
0

I really liked @tjeerdans answer which utilizes the EditorTemplate named String.ascx in the /Views/Shared/EditorTemplates folder. It seems to be the most straight-forward answer to this question. However, I wanted a template using Razor syntax. In addition, it seems that MVC3 uses the String template as a default (see the StackOverflow question "mvc display template for strings is used for integers") so you need to set the model to object rather than string. My template seems to be working so far:

@model object 

@{  int size = 10; int maxLength = 100; }

@if (ViewData["size"] != null) {
    Int32.TryParse((string)ViewData["size"], out size); 
} 

@if (ViewData["maxLength"] != null) {
    Int32.TryParse((string)ViewData["maxLength"], out maxLength); 
}

@Html.TextBox("", Model, new { Size = size, MaxLength = maxLength})
Community
  • 1
  • 1
zielot
  • 106
  • 1
  • 5
0

I solved it!!
For Razor the syntax is:
@Html.TextAreaFor(m=>m.Address, new { style="Width:174px" }) this adjusts the text area width to the width that i defined in the style parameter.
For ASPx the syntax is:
<%=Html.TextAreaFor(m => m.Description, new { cols = "20", rows = "15", style="Width:174px" })%>
this will do the trick