MVC4 Razor - #Html.DisplayFor not binding to model - asp.net-mvc

I am trying to find me feet with MVC4 Razor and I'm stuck with this simple problem.
When I use #Html.DisplayFor the model is always sent back as NULL, but when I use #Html.TextBoxFor this model is fully populated, what am I missing?
Thanks in advance

This is a common issue that many people miss in the asp.net mvc framework. Not just the difference in the helpers such as HiddenFor, DisplayFor, TextBoxFor - but how exactly the framework sets up automatically collecting and validating these inputs. The magic is all done with HTML5's data-* attributes. You will notice when looking at the input tag generated that there are going to be some extra properties in the form of data-val, data-val-required, and perhaps some additional data properties for types, for example numerics would be data-val-number.
These data attributes allow the jQuery extension jquery.validate.unobtrusive.js to parse the DOM and then decide which fields to validate or generate error messages.
The actual collection of posted data is reflected from the name property. This is what should map up to the model that is in the c# or vb [HttpPost] method.
Use HiddenFor when you want to provide posted data that the user does not need to be aware of.
Use DisplayFor when you want to show records but not allow them to be editted.
Use TextBoxFor when you want to allow user input or allow the user to edit a field.
EDIT
"the purpose of this view is to enable the user to view the data before submitting it to the database. Any ideas how I can achieve this?"
You could accomplish this with a duo of HiddenFor and DisplayFor. Use HiddenFor to have the values ready to be posted, and DisplayFor to show those values.

DisplayFor will not do the Model binding. TextBoxFor will do because it creates a input element in the form and the form can handle it when it is being posted. If you want to get some data in the HttpPost action and you dont want to use the TextBoxFor, you can keep that pirticulare model proeprty in a hidden variable inside the form using the HiddenFor HTML helper method like this.
#using(Html.BeginForm())
{
<p>The Type Name is</p> #Html.DisplayFor(x=>x.TypeName)
#Html.HiddenFor(x=>x.TypeName)
<input type="submit" value="Save" />
}

Use both DisplayFor and HiddenFor. DisplayFor simply displays the text and is not an input field, thus, it is not posted back. HiddenFor actually creates <input type="hidden" value="xxxx"/>

DisplayFor builds out a HTML label, not an input. Labels are not POSTed to the server, but inputs are.

I know this is a bit of an old question but you can roll your own, custom combined display control as shown below. This renders the model value followed by a hidden field for that value
#Html.DisplayExFor(model => Model.ItemCode)
Simply use what the framework already has in place
public static MvcHtmlString DisplayExFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> ex)
{
var sb = new StringBuilder();
sb.Append(htmlHelper.DisplayFor(ex));
sb.Append(htmlHelper.HiddenFor(ex));
return MvcHtmlString.Create(sb.ToString());
}

Do you mean during a form post? If you use DisplayFor, this creates a element which does not contain any form values. Typically you use these in conjunction with each other to create a label for your textbox, then using the Html.TextBoxFor to allow users to modify the data element.
Example:
#Html.DisplayFor(x=>x.Item)
#Html.TextBoxFor(x=>x.Item)
Will Render
Item <a text input field following>
Or in HTML
<label for="Item">Item</label><input type="Text" id="Item" name="Item"/>

Related

Post a collection of HttpPostedFileBase objects each having an additional value

I have an MVC 5 project with a form where the user can select multiple files to upload. However each file needs to have an additional integer value indicating its type. How do I implement this so the post method receives the collection of HttpPostedFileBase objects, along with each having the type value?
It will "just work", as long as your POSTed data is in the right format. The MVC model binder should just hook things up. If you've already gotten your controller action to receive an array of HttpPostedFileBase objects, you should just be able to have another argument to your action that is the array of integers `int[] fileTypes'. In your form, you will need input elements that contain those integers, with NAME attributes that allow the MVC model binder to bind the collection properly, which usually means you'd have input elements like this on your page:
<input type="hidden" value="3" name="fileTypes[0]" />
<input type="hidden" value="5" name="fileTypes[1]" />
With something like that you'd have a controller action that looked like
public ActionResult UploadMultipleFiles(HttpPostedFileBase[] uploadedFiles, int[] fileTypes)
The model binder will actually take care of hooking the input arguments up for you, as long as they were named properly on the HTML form.
If you are doing your upload via AJAX POST, the same still holds, you just have to make sure that you put the fileTypes values in as form variables with the right key names for collection binding.

Using HTML.EditorFor Vs using HTML.CheckBox

I have the following model class which contains a bool value:-
Public class Server
{
public bool IsIPUnique { get; set; }
}
Currently i am using the following to display the check box inside my Razor view:-
<input type="CheckBox" name="IsIPUnique" value="true" #(Html.Raw(Model.IsIPUnique ? "checked=\"checked\"" : ""))/> IP Unique.
but i read about the EditorFor template, and it can automatically create the check box and check/uncheck it based on the model values so i tried the following :-
#Html.EditorFor(model=>model.IsIPUnique)<span>test</span>
so my question is if i can replace my old code with the new one that uses the EditorFor ?, or asp.net mvc might deal with these values differently ?
Thanks
Basically you have 3 possibilities:
Write HTML manually (as you have done)
I would avoid writing the HTML manually if there is a HTML helper available.
Manually written HTML is prone to errors which can cause problems with model binding.
Use the specific HTML helpers (Html.CheckBoxFor)
The specific HTML helpers add a layer of abstraction to all controls.
It's easy to modify the template of all controls that use the same HTML helper and it makes your views more readable.
Use the general EditorFor
The EditorFor HTML helper is great if your model datatypes change often.
The EditorFor will adjust the input fields automatically to the new datatype and won't throw an error (as with the specific HTML helpers).
It is also a bit harder to add HTML attributes to the EditorFor while the specific HTML helpers often have overloads for them.
However, this is fixed in MVC 5.1: http://weblogs.asp.net/jongalloway/looking-at-asp-net-mvc-5-1-and-web-api-2-1-part-3-bootstrap-and-javascript-enhancements
Conclusion: in your case I would use the CheckBoxFor HTML helper because the datatype won't change likely and it will make the view cleaner

MVC #Html.Display()

I have something like:
<input type="text" name="TerrMng" id="TerrMng"/>
in HTML. What is the equivalent of the above using #Html.Display?
I tried using: #Html.Display("TerrMng", TerrMng)
but was not successful. Note that I like to use #Html.Display but not sure how to translate the ID value so that it shows up.
The Display method is not for creating input boxes. You'd want to use:
#Html.TextBoxFor(m => m.TerrMng);
or the templated helper method:
#Html.EditorFor(m => m.TerrMng);
I'm assuming that you want to use modelbinding. If not, if you really just want to use a helper to simply make an input tag, use:
#Html.TextBox("TerrMng");
This would be sent to the client:
<input id="TerrMng" type="text" value="" name="TerrMng">
The first 2 methods above would result in the exact same html, if model.TerrMng was "" or String.Empty. If for some reason you don't want the value attribute, you'll need to type it out yourself.
This should do the trick if you are just wanting to display the data and not allow the user to edit the information.
#Html.DisplayFor(m => m.TerrMng);
Edit:
what-is-the-html-displayfor-syntax-for is another question on stackoverflow that may give you some more guidance.
Edit:
TerrMng does not exist on PageLoad so you cannot use the Html.Display in that way. You need to create it and fill its value with the value received from the jQuery. In this case where you would have to do the following:
HTML
#Html.Display("TerrMng"); // This creates the label with an id of TerrMng
jQuery
$("#TerrMng").val(TerrMng); // This puts the value of the javascript variable into the label
You could try something based on this. This is not exact but you could get some idea.
#Html.TextBoxFor(yourmodel => model.yourModelFieldname, null)
#Html.Display() is used instead of #Html.DisplayFor() when your model is not known at compile time, or if you prefer to work with strings, rather than with strong types. For example, these 2 are equivalents (given that your model is some class):
#Html.DisplayFor(m => m.MyProperty)
and
#Html.Display("MyProperty")
But the additional cool feature of the Display() method is that it can also do the lookup in the ViewData, and not just in your Model class. For example, here is a way to display the HTML for the property on a random object, given that we know it has a property named "Blah" (the type of the object doesn't really matter):
#{ ViewData["itsawonderfullife"] = SomeObject; }
<div>#Html.Display("itsawonderfullife.Blah")</div>
This way, we are telling HtmlHelper to look into the ViewData, instead of our Model, and to display the property Blah of a given SomeObject.

Differences between Html.TextboxFor and Html.EditorFor in MVC and Razor

Why by default were these changed when adding a new "edit" view? What are advantages when using EditorFor() vs. TextboxFor()?
I found this
By default, the Create and Edit scaffolds now use the Html.EditorFor helper instead of the Html.TextBoxFor helper. This improves support for metadata on the model in the form of
data annotation attributes when the Add View dialog box generates a view.
Quoted from here.
The advantages of EditorFor is that your code is not tied to an <input type="text". So if you decide to change something to the aspect of how your textboxes are rendered like wrapping them in a div you could simply write a custom editor template (~/Views/Shared/EditorTemplates/string.cshtml) and all your textboxes in your application will automatically benefit from this change whereas if you have hardcoded Html.TextBoxFor you will have to modify it everywhere. You could also use Data Annotations to control the way this is rendered.
TextBoxFor: It will render like text input html element corresponding to specified expression. In simple word it will always render like an input textbox irrespective datatype of the property which is getting bind with the control.
EditorFor: This control is bit smart. It renders HTML markup based on the datatype of the property. E.g. suppose there is a boolean property in model. To render this property in the view as a checkbox either we can use CheckBoxFor or EditorFor. Both will be generate the same markup.
What is the advantage of using EditorFor?
As we know, depending on the datatype of the property it generates the html markup. So suppose tomorrow if we change the datatype of property in the model, no need to change anything in the view. EditorFor control will change the html markup automatically.
The Html.TextboxFor always creates a textbox (<input type="text" ...).
While the EditorFor looks at the type and meta information, and can render another control or a template you supply.
For example for DateTime properties you can create a template that uses the jQuery DatePicker.
This is one of the basic differences not mentioned in previous comments:
Readonly property will work with textbox for and it will not work with EditorFor.
#Html.TextBoxFor(model => model.DateSoldOn, new { #readonly = "readonly" })
Above code works, where as with following you can't make control to readonly.
#Html.EditorFor(model => model.DateSoldOn, new { #readonly = "readonly" })
There is also a slight difference in the html output for a string data type.
Html.EditorFor:
<input id="Contact_FirstName" class="text-box single-line" type="text" value="Greg" name="Contact.FirstName">
Html.TextBoxFor:
<input id="Contact_FirstName" type="text" value="Greg" name="Contact.FirstName">

LabelFor and TextBoxFor don't generate the same id's

When using the following code, the id's of the field and the id in the for attribute of the label isn't identical.
<%: Html.LabelFor(x => x.Localizations["en"]) %> => Localizations[en]
<%: Html.TextBoxFor(x=> x.Localizations["en"]) %> => Localizations_en_
<%: Html.LabelFor(x => x.Localizations["en"].Property) %>
=> Localizations[en]_Property
<%: Html.TextBoxFor(x=> x.Localizations["en"].Property) %>
=> Localizations_en__Property
I traced the code in reflector and saw that the way the values are generated are different. Not using the same helper method.
LabelFor uses HtmlHelper.GenerateIdFromName and TextBoxFor uses TagBuilder#GenerateId.
Does anyone know the reason for this, or a workaround (except writing your own entire set of input/textarea/select helpers)? Or is it a bug?
UPDATE:
Since I anyway use a html helper for the label with a second parameter for the label text, I did modify it to use the same id generation code as the form field helpers.
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, string labelText)
{
// The main part of this code is taken from the internal code for Html.LabelFor<TModel, TValue>(...).
var metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
var fieldName = ExpressionHelper.GetExpressionText(expression);
TagBuilder builder = new TagBuilder("label");
// Generate the id as for the form fields (adds it to self).
builder.GenerateId(fieldName);
// Use the generated id for the 'for' attribute.
builder.Attributes.Add("for", builder.Attributes["id"]);
// Remove the id again.
builder.Attributes.Remove("id");
builder.SetInnerText(labelText);
return MvcHtmlString.Create(builder.ToString());
}
This solves my immediate problem, but it doesn't answer the question as to why the implementation looks like it does in MVC2. If there's an reason for it.
By the way: There's no need to actually modify the id/for attribute in HTML5, since it's perfectly legal to have an id looking like ^~[] if you'd like to. All major browsers support it. This is nicely explained by Mathias Bynens.
UPDATE 2:
This does not solve the problem at all actually, since the DefaultModelBinder can't bind to it anyway. Using nested objects in dictionaries doesn't seem to be supported by the field name generator in MVC 2, since it generates:
<input type="text" name="Dict[en]" value="(VALUE)">
Instead of what the model binder wants:
<input type="hidden" name="Dict[0].Key" value="en">
<input type="text" name="Dict[0].Value" value="(VALUE)">
Strange that it comes out of the box this way.
I've tried create a custom model binder for it, but I can't get MVC2 to use it whatever I try to use it on:
ModelBinders.Binders.Add(typeof(IDictionary<string,object>), new DictionaryModelBinder());
ModelBinders.Binders.Add(typeof(IDictionary<string,string>), new DictionaryModelBinder());
ModelBinders.Binders.Add(typeof(IDictionary), new DictionaryModelBinder());
ModelBinders.Binders.Add(typeof(Dictionary), new DictionaryModelBinder());
So right now it looks like it's back to manually creating the name attribute values with hidden .Key fields.
This is a bug in MVC3 that we are planning on fixing for the next release (MVC 3 RTM). LabelFor will go through the tagbuilder to generate the 'for' attribute using the same logic that is used to generate ids so they will line up for arrays and nested types.
We are currently using the html 4.01 spec to generate ids so you cannot use ids that begin with non-letters. We will think about what the best approach should be now that the standards have changed.
MVC intentionally mungs ids with special characters which are significant in jQuery/CSS3 selectors. This is because the selector syntax becomes complicated when there are "reserved" (by jQuery/CSS3) characters in the ID.
It does not do this with name because it isn't necessary there and gets in the way of binding.
It's certainly a bug if LabelFor doesn't actually point to the corresponding TextBoxFor. But I'd argue the bug is in LabelFor, not in TextBoxFor.

Resources