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
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'm trying to send back the contents of the input field as follows.
#model Bike
#using (Html.BeginForm("BikeStore", "Home", FormMethod.Post))
{
<input type="text" value="#Model.Color" />
<input type="submit" value="Save"/>
#Html.ActionLink("Cancel", "Bikes", "Home")
}
The action and the model are declared as follows.
public ActionResult BikeStore(Bike bike)
{
...
return RedirectToAction("Bikes");
}
public partial class Bike
{
[Key] public Guid Id{get; set;}
[Required, StringLength(999)] public string Color { get; set; }
}
I'm hitting the breakpoint in the method BikeStore but bike passed in is empty, i.e. it's not null but all the strings are, the guids are 00..00 etc.
I've tried different variable types. I also tested FormMethod.Get and (not at the same time, of course) adding HttpPost attribute. No luck.
Asp.Net MVC binder system uses the name of the input elements to bind to the appropriate property or parameter. So, change this line:
<input type="text" value="#Model.Color" />
to:
#Html.TextBoxFor(m => m.Color)
This will generate following html(for example):
<input type="text" id="Color" name="Color" value="Black" />
Keep in mind that, you can use another helper which offers you to hard-type the name of the value:
#Html.TextBoxFor("Color")
Or, you can write plain html as you did and add name attribute, but let Asp.Net decide what must be the name of the element.
<input type = "text"
name = "#Html.NameFor(m => m.Color)"
value = "#Model.Color" />
If we want to summarize the result of the answer, then let's write Pros and Cons of each version:
Strongly typed version - These helpers can be used only with strongly typed views. The HTML generated by these helpers is not any different, but we use the strongly typed helper methods in our projects because they reduce the chances of causing an error by mistyping a property name.
Hard-typed version - The string argument is used to search the ViewData, ViewBag, and view model to find a corresponding data item that can beused as the basic for the input element. So, for example, if you call
#Html.TextBox("DataValue"), the MVC Framework tries to find some item of data that corresponds with the key DataValue. The following locations are checked: ViewBag.DataValue and Model.DataValue.
The first value that is found is used to set the value attribute of the generated HTML. (The last check, for #Model.DataValue, works only if the view model for the view contains a property or field called DataValue.)
If we specify a string like DataValue.First.Name, the search becomes more complicated. The MVC Framework will try different arrangements of the dot-separated elements, such as the following:
• ViewBag.DataValue.First.Name
• ViewBag.DataValue["First"].Name
• ViewBag.DataValue["First.Name"]
• ViewBag.DataValue["First"]["Name"]
Also keep in mind that, the first value that is found will be used, terminating
the search.
So this is a bit of a 'how can I be lazier?' or 'can I be super overprotective?' type of question. The scenario I'm trying to account for is this...
A model is updated with a new field, but for whatever reason the addition of the property in the model's update user interface is forgotten while the explicit setting of it is implemented in the db access controller. When the user clicks submit, the value of this property makes it to the controller as 'null' and summarily the database value is 'updated' to null.
Is there a way to get the properties on the model that are not explicitly put into the form, and add hiddenfors for these properties? (Worst case scenario, this value is not updated as opposed to losing data).
Edit: Potential Scenario
Initial object (used as the model on the form)
public MyObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
The form has
#Html.TextboxFor(model => model.Value1)
#Html.TextboxFor(model => model.Value2)
Later, someone comes along and adds
public string Value3 { get; set; }
to the MyObject, but forgets to add a
#Html.TextboxFor(model => model.Value3)
This results in Value3 being submitted as null when the form is submitted. What I'm trying to figure out is if it is possible to add something to the form similar to the following:
foreach (var nonExplicitlyUsedProperty in Model.Properties)
{
#Html.HiddenFor(model => model.nonExplicitlyUsedProperty)
}
In the above scenario, this would be the equivalent of adding
#Html.HiddenFor(model => model.Value3)
at the bottom of the form.
You have a couple of options:
Client-Side Validation
Server-Side Validation
Server-Side Sanitize
It sounds like your utilizing a Form Action which will automatically submit data to your Controller. You'll either need to validate the data from the Client before it is passed to the server.
Another approach would be to simply validate the parameter, to avoid a Null being passed or modify your SQL to ignore a Null value passed to it before it does the update.
The other approach would be to couple the functionality of the UI in a manner that is better represented for your Model and Controller. This way it is far more fluid and easy to understand.
Important:
You can use a Hidden Field to store data in when the model is initialized, but your introducing extra data and need to cater / regulate before you can proceed. Though the hack may solve your problem, it isn't ideal truly as it bypasses your problem rather then truly solving it.
#if(Model != null)
{
foreach(var content in Model)
{
<div>
<input type="hidden" id="hdName" name="name" value="#content.Name" />
<div>
}
}
By using this hidden field, you'll then have to ensure when the user does change the field, the hidden field is updated like so:
$('#txtName').blur(function () {
$('#hdName').val($(this).val());
});
As you can see how using Hidden Fields can truly become a nightmare, so I suggest you rethink your solution.
So here is the final answer for what I have been trying to do...
First build out the form (as normal).
Then generate hidden input fields for all of the properties on the model like so... using string format to give it slightly different name/id attributes from the explicitly defined input fields' name/id. (Since ASP.Net passes values from view to controller by name instead of by id, but jQuery standard seems to be to use ids instead of name, account for both situations)
#foreach (var property in Model.GetType().GetProperties())
{
<input type="hidden" name="#(String.Format("{0}_2", property.Name))" id="#(String.Format("{0}_2", property.Name))" value="#property.GetValue(Model, null)" />
}
Afterwards, use jQuery to iterate through the hidden inputs, remove the discriminator that was added to the "name" property to get the actual property name, then check if the input with id of that value (the original property name) has a value. If it DOES have a value, the hidden input is not needed and can be removed, otherwise, rename the hidden input name attribute (since that's how ASP.Net passes the value to the controller) to the original property name.
<script>
$(document).ready(function() {
$("input[type='hidden']").each(function() {
var actualPropertyName = $(this).attr("id").substr(0, ($(this).attr("id").length - 2));
var value = $("#" + actualPropertyName).val();
if (value != null) {
$(this).remove();
} else {
$(this).attr("name", actualPropertyName);
}
});
});
</script>
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)
I have an input, type text, element which is being validated using MVC3 validation on the client and I’d like to not have the input value sent to the server when the post occurs.
I have two entities: A “File” entity and a “Company” entity which share a 1 to 1 relationship. The file entity has a CompanyId foreign key.
This is why if you look at the name and id attributes they appear as: File.Company.Code or File_Company_Code.
The reason I want to avoid sending the input value back to the server is when the request reaches the server I only want to bind the values to my entity of type “File”. As it is also receiving a value for “File.Company.Code” it is also attemting to bind the values to the File’s company object, which is what I want to avoid.
The input element is :
<input name="File.Company.Code" id="File_Company_Code" type="text" data-val-required="Se requiere un código de cliente." data-val="true" value=""/>
And the span element:
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for=" File.Company.Code "/>
I’ve tried:
-Changing the input name and span data-valmsg-for attributes using jquery. But I think that after doing this I may need to rebind the validators??
Any suggestions? (I hope to have explained myself clearly if not let me know.)
Thanks
UPDATE 1 **
Thanks to AFinkelstein sugestion which put me on the right track I updated my domain model such as:
public class FileModel {
public File File {
get {
return this.file;
}
}
*** ADDED this which helped me solve the problem ***
public Company Company {
get {
return this.file.Company;
}
}
}
In my view instead of doing :
#Html.TextboxFor(model => model.File.Company.Code)
#Html.ValidationMessageFor(model => model.File.Company.Code)
I now do:
#Html.TextboxFor(model => model.Company.Code)
#Html.ValidationMessageFor(model => model.Company.Code)
This way the generated name and Id attributes have the value: Company.Code and Company_Code, they dont have the preceding "File". When I receive the post on the server and bind the values to the File object:
FileModel fileModel = new FileModel();
try {
TryUpdateModel(fileModel.File, "File");
as it is not receiving a value for "File.Company.Code" it doesnt attempt to initialize the file's "Company" object, which was causing me other problems.
As it is also receiving a value for “File.Company.Code” it is also attemting to bind the values to the File’s company object, which is what I want to avoid.
I presume this means that File is a domain model within your project. I recommend using a view model in your view.
public class FileViewModel
{
//other stuff contained within the File class
[Required]
public string FileCompanyCode { get; set: }
}
You can use your view model to create or refetch your actual File after posting. Just don't set your actual file company object to the file company code property in the view model. This way it doesn't actually matter if your file company code is binded or not.
I had a similar issue where I wanted the client-side validation but not the field being posted as it was a list of objects and the posting structure didn't support a normal validator.
Still, I was inspired by this question and answer and found out a solution where you add another input field (with all the HTML5 tags that an HTML.HiddenFor would have generated to enable unobtrusive validation) and Html.Validator for the non-existing model property hence the MVC binder in the postback would ignore it.
I also added an Html.ValidatorFor for the real property so that the validation on postback would have somewhere to render as my other validation tags point to a different tag (theoritically)