I create some helper method and create TextBoxFor inside it.
input.AppendLine(html.TextBoxFor(expression, format, attributes).ToString());
My format was "{0:dd/MM/yyyy}". It's work totally fine and render as
<input class="form-control req" data-val="true" data-val-date="The field detr must be a date." id="detr" name="detr" type="text" value="25/05/2558" />
But when ModelState has error on this field, TextBoxFor was totally ignore my format and rendered as
<input class="input-validation-error form-control req" data-val="true" data-val-date="The field detr must be a date." id="detr" name="detr" type="text" value="28/5/2558 0:00:00" />
What happened here?
How can I fixed this?
Thank you in advance.
I finally found source of this strange phenomenon.
It's happend when i use FluentValidator and explicitly call
validator.validate(obj)
When i add it's into ModelState, ModelState's property RawValue data type was DateTime. and that mess everything up. (if not explicitly call but set validator as class attribute RawValue data type will be string)
So I found 2 ways to fix this problem.
1st: Just set validator as class attribute and not explicitly call validator.
[FluentValidation.Attributes.Validator(typeof(objValidator))]
2nd: Create new ModelState and manually add model error.
var _validator = new ObjValidator();
var results = _validator.Validate(dto);
ModelStateDictionary modelStateDicts = new ModelStateDictionary();
foreach (KeyValuePair<string, ModelState> kv in ModelState.Where(x => results.Errors.Count(y => y.PropertyName == x.Key) > 0))
{
modelStateDicts.AddModelError(kv.Key, results.Errors.FirstOrDefault(x=>x.PropertyName == kv.Key).ErrorMessage);
}
Related
I have model property int? CaseId
public class TaskDetailsVm
{
public TaskDetailsVm(Task task)
{
CaseId = task.CaseID;
}
public int? CaseId { get; set; }
}
and on view:
#Html.HiddenFor(x => x.CaseId) => 0
#Html.Hidden("CaseId", Model.CaseId) => 0
#Html.Hidden("qwe", Model.CaseId) => real value
<input type="hidden" id="CaseId" name="CaseId" value="#Model.CaseId" /> => real value
in browser I see this:
<input data-val="true" data-val-number="The field CaseId must be a number." id="CaseId" name="CaseId" type="hidden" value="0">
<input id="CaseId" name="CaseId" type="hidden" value="0">
<input id="qwe" name="qwe" type="hidden" value="22906">
<input type="hidden" id="CaseId" name="CaseId" value="22906">
Why can I see the following? I don't see any scripts to override this value. And how can I resolve it?
Also for first line of code I see additional attributes data-val="true" and data-val-number="The field CaseId must be a number." for some reasons that I can't understand.
This has to do with the ModelState. As per this article:
ASP.NET MVC assumes that if you’re rendering a View in response to an HTTP POST, and you’re using the Html Helpers, then you are most likely to be re-displaying a form that has failed validation. Therefore, the Html Helpers actually check in ModelState for the value to display in a field before they look in the Model. This enables them to redisplay erroneous data that was entered by the user, and a matching error message if needed. Since our [HttpPost] overload of Index relies on Model Binding to parse the POST data, ModelState has automatically been populated with the values of the fields. In our action we change the Model data (not the ModelState), but the Html Helpers (i.e. Html.Hidden and Html.TextBox) check ModelState first… and so display the values that were received by the action, not those we modified.
Now in this case: #Html.HiddenFor(x => x.CaseId, new {Value = #Model.CaseId}), since you are explicitly defining a value for the current Model, it displays the value that you expect. You can use ModelState.Clear(); in your Controller after your POST on the form to reset the model values.
I am trying to pass extra attribute 'data_id 'in #HTML.TextBoxFor... but I am getting no result
what I am missing in following code...
#Html.TextBoxFor(model => model._MarkScheme.MarkSchemeId, new { id = "_MarkSchemeId_Input", #class = "ElementMarkingSchemeTitle k1-grid-input k-textbox_3", data_id = #item.ElementID + "EMST"})
Many Thanks
Underscores aren't valid in HTML attribute names so Razor converts it to a hyphen. This will render with a data-id attribute.
HTML 5 data-* attributes are expected to be data_* in ASP.Net MVC view engine to render it.
HTML syntax
<input type="text" data-my-id="5" value="something" />
MVC syntax
#Html.TextBoxFor(model => model.Name, new {data_my_id=#item.ID})
Your case it is data-id
I have a model with one of the property of type object . This property is a dynamic property and could sometime contain a string or a date or a Boolean.
I have a editor template for each type i.e boolean , string , date etc .
The problem I have is when the page is posted , the postback contains a array instead of the actual value. The first element of the array contains the actual value.
Why is the value being returned as a array ?
My model
public string Description;
public string Name { get; set; }
public Type Type{ get; set; }
object _value;
public object Value { get;set;}
statement in the view
#Html.EditorFor( m => m.Value)
Edit : Corrected the object name from _value to Value. It was a wrong Ctrl V operation.
Edit : The HTML rendered in the browser
When the object contain a boolean value (checkbox):
<div>
<input checked="checked" data-val="true" data-val-required="The BoolJPY field is required." id="FurtherInformationFieldObject_Properties_1__Value" name="FurtherInformationFieldObject.Properties[1].Value" type="checkbox" value="true"><input name="FurtherInformationFieldObject.Properties[1].Value" type="hidden" value="false">
When the object contains a string(Textbox) :
<div id="divStringField"><input class="text-box single-line valid" data-val="true" data-val-required="The String Field field is required." id="FurtherInformationFieldObject_Properties_2__Value" name="FurtherInformationFieldObject.Properties[2].Value" type="text" value=""> </div>
Edit 2 : Posting the complete model and view code.
Controller code :
public ActionResult Edit(string name ="field1" )
{
Models.DynamicData data1 = new Models.DynamicData();
//all this comes from the database table. I am putting the value directly in field just for simplicity
// this is exactly how I convert the value from the entity to the model
data1.Description = "Field1 Description";
data1.Name = "field1";
data1.Type = typeof(string);
data1.Value = Convert.ChangeType("MyStringValue", data1.Type);
//similarly add few more fields to the model collection
return View(data1);
}
[HttpPost]
public ActionResult Edit(Models.DynamicData model)
{
// break point here : model.Value shows a array of string instead of the edited value.
return View(model);
}
View :
#model SampleDynamicDataProject.Models.DynamicData
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>DynamicData</legend>
<div class="editor-label">
#Model.Description
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Value)
#Html.ValidationMessageFor(model => model.Value)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
I should explain I used object as type for Value property because the value could be string or bool or date ex data1 in above controller could look like below
data1.Description = "Field2 Description";
data1.Name = "field2";
data1.Type = typeof(bool);
data1.Value = Convert.ChangeType("true", data1.Type); // database stores "true" as string which is converted into a boolean and stored in the object.
As shown in the code , my problem is in the post action for Edit , I get Value as an array even for a simple string.
The sample project code here https://drive.google.com/file/d/0B3xCaeRk2IQZSTM0aHdoWEtNYW8/edit?usp=sharing
I got a answer to my question at one of the other forums.
Basically the reason MVC binder is returning a array is because it does not understand what type of data/control is used in the html and the model binder fails.
I got around my issue by modifying the model to have two different property a
public String StringValue
public Bool BooleanValue
I use the StringValue field when the Type is String , Date , Number etc.
I use the BooleanValue for field with Type as Boolean.
Its not the cleanest approach but it will have to do till the point I write my own custom model binder.
Thanks to bruce who answered my question here http://forums.asp.net/p/1961776/5605374.aspx?Re+MVC+4+Postback+returns+a+array+for+property+of+type+object+in+my+model
I now understand why the model binder fails.
Pasting his answer here for the benefit of others
you need to understand how browser postback is done. on form submit a collection of name/value pairs is sent. the name is the form element name, the value is the elements value. standard url encoding is done. so for:
the postdata is
foo=1&bar=true
note the post data is just a string with no type data. the brwser allows duplicate name, so
the post data is:
foo=1&foo=true
when asp.net load the post data into the form collection (which is just a dictionary), it can not add the key "foo" twice, but concats the values seperated by a "," ("1,true"). the binder just treats it as a string array named foo with 2 values.
now we get to another browser behavior. form elements that support checked (radio and checkbox) are only include the post data if checked. this causes a problem for the mvc binder with checkbox, becuase it can not tell from the postback data if the element was not checked or not included. this is important if you are using tryupdate to apply only a subset of the model properties, becuase only a subset was rendered. to get around this, the checkbox helper renders two fields with the same name, a hidden with the value "false" and a checkbox with the value "true".
I am trying to create a form with MVC4 and so far it is going well, but I have run into a problem where is doesn't seem possible to give my inputs a default value by setting the associated 'value' attribute.
The code that I am using:
#Html.TextBoxFor(x => x.Name, new { value="xxx" })
renders the following:
<input id="EditDetail_SKU" name="EditDetail.SKU" type="text" value="" />
That is kind of annoying because the value attribute is now empty. It seems that I can add all sorts of attributes after the fact, and they are respected, ala:
#Html.TextBoxFor(x => x.Name, new { value="xxx", thing="whatever",foo="bar" })
Yields:
<input foo="bar" id="name" name="Name" thing="whatever" type="text" value="" />
Those attributes are even made up, but they are still respected, so why is 'value' being ignored in this case, and is there something I can do prevent this?
Please refer to this thread:
How to set a default value with Html.TextBoxFor?
The quick and dirty method would be:
<%= Html.TextBoxFor(x => x.Age, new { #Value = "0"}) %>
Notice the # and capital V
The default value will be the value of your Name property inside model.
This is how strongly typed models work in ASP.NET MVC.
In your respective action assign the Name variable to default value.
public ActionResult Index()
{
var model = new YourModel();
model.Name = "Default Value";
return View(model);
}
And in your view,
#Html.TextBoxFor(x => x.Name)
Use #Value instead of value. value is a keyword.
I have a dictionary property called Week:
public IDictionary<DayOfWeek, Day> Week { get; private set; }
And I'm trying to pass its values off to HiddenFor (Days)
#Html.HiddenFor(x => x.Week.Values)
It has to be a property of the Model so I can't do x.Week.Values.ToList();
How would I go about passing the Dictionary Values to the Html.HiddenFor?
Well since your using HiddenFor, I'm going to assume that you need to rebind the values of the dictionary on form post. To bind a Dictionary to the view, your going to need to do something like this:
#foreach (var key in Model.Week.Keys)
{
Html.DisplayFor(model=>model.Week[key]);
}
Each value in the dictionary will be given its own Hidden Input field, with the name attribute:
name="Week.{key here}
If, on the other hand, all you need to do is send the data in your model to the client so that you can do something with it in JavaScript, you might want to look at writing it to the page as JSON.
<script type="text/javascript">
#Html.Raw(Json.Encode(Model.Week))
</script>
I would use:
#foreach (var key in Model.Week.Keys)
{
#Html.HiddenFor(model=>model.Week[key]);
}
The '#' before the Html seems necessary for razor to output the hidden fields.
It outputs something like this for each key:
<input data-val="true" data-val-number="The field Int32 must be a number."
data-val-required="The Int32 field is required." id="Week_5_" name="Week[5]"
type="hidden" value="3">