In a view I'm using this overload of HtmlHelper.TextBox:
public static MvcHtmlString TextBox(
this HtmlHelper htmlHelper,
string name,
Object value
)
The documentation states:
value
Type: System.Object
The value of the text input element. If this value is null, the value of the element is retrieved from the ViewDataDictionary object. If no value exists there, the value is retrieved from the ModelStateDictionary object.
I do provide a value when I call this overload, and this value is not null. Nevertheless, the value for the textbox is retrieved from the ModelStateDictionary whenever it is present there.
In order to force the textbox to use the value provided inline, I have to reset the model first in the controller (or remove the key with the textbox's name from the keys collection).
Same applies to other controls rendered by HtmlHelper.
Where is my understanding wrong? Or is that a bug in the documentation?
ModelState always has precedence in all input helpers, not just TextBox. This allows you to specify a default value in the view, and after POST render the posted value (taken from ModelState) without having to manually pass the posted value to the view. To use a different value you must remove the entry from ModelState.
This is how the model binder works, it takes the values from the modelstate, probably to preserve the values from a failed postback or validation failure. Have you tried using the Html attributes to set the value
Html.TextBox("Id", new { Value = Model.Id})
Si
Maybe this will help.
Using viewmodel with property below and ViewData["textboxvalue"]
public string Id { get; set; }
If you have provided an object that can be bound into the "value" parameter, the the html helper will always use its value.
-Text box value bound to view model - these show the model's id
#Html.TextBox("Id", Model.Id)
-Text box with provided value from view data - these show the view data's value
#Html.TextBox("Id", ViewData["textboxvalue"])
Even if the name provided is not the same as the property name, the value will still be that provided by the model
-Text box value bound to view model - these show the model's id
#Html.TextBox("Id2", Model.Id)
-Text box with provided value from view data - these show the view data's value
#Html.TextBox("Id2", ViewData["textboxvalue"])
If you provide your own custom
-Text box with provided hardcoded value - this shows the string provided in view
#Html.TextBox("Id", "my hardcoded value in view")
The documentation is stating - if you do not populate the "value" parameter, as in the below:
#Html.TextBox("Id", null)
Then mvc framework will automatically try and search both the model that is bound to the view and the data dictionary.
If you have this case, then
-Found a property called "Id" in view model. Text box value set to the model's value
#Html.TextBox("Id", null)
But if the "name" value is not a property/key in the model/dictionaries, then the framework could not find any value, so the text box value will be an empty string
-Text box with name that is not property in view model or view data
#Html.TextBox("Id2", null)
Related
I am working on an ASP.NET MVC-4 web application. I'm defining the following inside my action method to build a SelectList:
ViewBag.CustomerID = new SelectList(db.CustomerSyncs, "CustomerID", "Name");
Then I am rendering my DropDownListFor as follow inside my View:
#Html.DropDownListFor(model => model.CustomerID, (SelectList)ViewBag.CustomerID, "please select")
As shown I am naming the ViewBag property to be equal to the Model property name which is CustomerID. From my own testing, defining the same name didn't cause any problem or conflict but should I avoid this ?
You should not use the same name for the model property and the ViewBag property (and ideally you should not be using ViewBag at all, but rather a view model with a IEnumerable<SelectListItem> property).
When using #Html.DropDownListFor(m => m.CustomerId, ....) the first "Please Select" option will always be selected even if the value of the model property has been set and matches one of the options. The reason is that the method first generates a new IEnumerable<SelectListItem> based on the one you have supplied in order to set the value of the Selected property. In order to set the Selected property, it reads the value of CustomerID from ViewData, and the first one it finds is "IEnumerable<SelectListItem>" (not the value of the model property) and cannot match that string with any of your options, so the first option is selected (because something has to be).
When using #Html.DropDownList("CustomerId", ....), no data-val-* attributes will be generated and you will not get any client side validation
Refer this DotNetFiddle showing a comparison of possible use cases. Only by using different names for the model property and the ViewBag property will it all work correctly.
There is not harm to use it. You will not get any error. but best practice is to bind model property.
I have table record as follows
this how those two properties defined in model classes
public bool? Report_Users { get; set; }
public bool? Innovation_Discussion_User { get; set; }
this is Form syntax that used to populate Boolean values in checkboxes
#Html.CheckBox("Report User", Model.Report_Users.GetValueOrDefault(), new { #value = "false" }) Report User
#Html.CheckBox("Innovation Discussion User", Model.Innovation_Discussion_User.GetValueOrDefault(), new { #value = "false" }) Innovation Discussion User
This is check boxes view of that form
but once I submit , I can see in model those two properties values getting null in POST method
How can I bind this check-boxes values to Boolean property fields, properly ?
There a a number of issues with you implementation.
Your CheckBox("Report User", ...) method is generating a checkbox (and associated hidden input) with name="Report" and your model does not contain a property named Report. Because no value is posted back for Report_Users, its value will always be its default (null) in the POST method. In order to bind to bool? Report_Users then it would need to be CheckBox("Report_User", ...) (underscore not space).
Changing that however means the value of Report_User now always be false instead of null because you have added new { #value = "false" }. The CheckBox() method generates 2 inputs, <input type="checkbox".. value="True" /> and <input type="hidden".. value="False" /> in order to correctly bind to your property. You have now overridden it to generate <input type="checkbox".. value="false" /> so a value of false is posted even if the checkbox is checked.
But the real issue with your code is that your property is bool? (nullable) and CheckBox() is designed to bind to bool. typeof bool? has 3 states (true/false/null) whereas a checkbox has 2 states (on/off or true/false). Its not clear why your database property is nullable, but if you cannot change it, then (as always), use a view model with a bool property. In the GET method, when mapping your data model to the view model, you can use
myViewModel.ReportUser = myDataModel.Report_User.GetValueOrDefault();
and now you can build you view correctly using the strongly typed helper
#Html.CheckBoxFor(m => m.ReportUser)
#Html.LabelFor(m => m.ReportUser, "Report User") // use a label
// or add [Display(Name="Report User")] and use #Html.LabelFor(m => m.ReportUser)
What to learn from this:
If your model is not binding, always check the html your generating
(the name attributes of the form controls must match the name of
the property)
Always use the strongly typed xxxxFor() HtmlHelpers to generate
form controls. Not only do you get intellisense, it ensures that the
correct name attributes are generated, and any errors are reported
Never attempt to change the value attribute when using the
HtmlHelpers (or the name attribute). The helpers generate the
correct html based on the model property and changing them from the
default values will only lead to model binding failing
I use pure html since I do not like the #Html class.
How do I get the value from an option in a select input ?
The model binder gives me the text property of an option and not the value, as I would have liked :)
Do I really have to use Request.From[""] to do it ?
Thanks!
EDIT
This will make the text attribute, not the value attribute, appear in the viewmodel
public ActionResult UpdateCard( UpdateCreditCardViewModel form )...
And if I just add another parameter with the input name, then the viewmodel is correcly populated. I don't have to do anything else it seems.
public ActionResult UpdateCard( UpdateCreditCardViewModel form, string Country )...
I guess that's the default behaviour ?
I am using ASP.NET MVC4 with .NET Framework 4.5. I have a controller action that accepts a model of one type with a property named 'Name' but renders a view using a model of another type. I am still able to use #Html.TextBox("Name") and #Html.ValidationMessage("Name").
I want the textbox to display the sanitized input, that is, the input without leading/trailing/extra spaces the user may have entered. The setter for my model sanitizes the value for me, and I am successfully obtaining the sanitized value using the getter within the controller action. It's just that upon submitting the form, the textbox still displays the unclean input.
Is there some mechanism I am missing? Is the #Html.TextBox(string name) helper looking at the raw request data and not the model? If so, how come the validation message is working?
Update
I have just tried defining a new view model that includes my textbox field so I could hopefully just use the #Html.TextBoxFor helper. Everything is still working as it was after a re-build, I am still not getting sanitized input appearing in the textbox. I still don't know a solution for this.
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult MyAction(MyViewModel model)
{
if (this.ModelState.IsValid)
{
using (var service = new MyService())
{
model.MyResults = service.DoSomething(model.MySanitizedProperty);
}
}
return this.View("MyView", model);
}
Then, in "MyView":
#Html.TextBoxFor(m => m.MySanitizedProperty)
<input type="submit" value="Go" />
#Html.ValidationMessageFor(m => m.MySanitizedProperty)
In the controller, invoking model.MySanitizedProperty returns the sanitized value while the textbox goes on to display the unsanitized data.
It sounds like a problem with the models; make sure you are properly accessing the value from the model you wish to populate it with, i.e., possibly discretely specifying the model "Name" is coming from.
Also, check to see that the setter has a chance to operate on the value - if the controller is activating before the setter function is used, then you'll only get the original input value.
Realize you have to go to the server for the setter to work, possibly you need a async postback or such, and the value reloaded.
I have a database first design. I want to have default values for my model so that a user can just leave an editor textbox blank for one of my nullable fields. EF doesn't seem to want to just throw null into the database, so I was hoping I could set default values to null for my nullable attributes.
Ie I want to insert null into my database for an attribute if the textbox is left blank when submitted.
I've read you can add a contructor to a partial class to do what I want. So you would basically have:
public partial class MyClass{
public MyClass()
{
field1 = null; //this would be the default value for field1
}
}
The only problem is the autogenerated partial class for the model I'm working on already has a constructor, so I can't add a constructor to a different (permanent) partial class. I don't want to update the autogenerated partial class because it will just be overwritten when I update my edmx from the database.
The default value of the string should already be null, so I think you are taking the wrong path.
The problem is a textbox will always be passed with a form post, even if it is blank, which will result in an empty string in your controller rather than a null value, even if you find a way to explicitly set your string to null in your initial class.
Instead, check the value of the string in your controller after the form is posted back, and if it is empty, set it to null.
if (String.IsNullOrEmpty(formModel.MyString)) formModel.MyString = null;
Either that, or create a default model binder to check the value of the field during model binding, and not set it if it is empty.