Define editor template that makes use of ModelState - asp.net-mvc

Please help me to implement the editor template when Model and ModelState mismatch.
For example: Model.A == 'x', ViewData.ModelState['A'] == 'y'.
That can occur when form was posted invalid.
Native templates take this in account and display right value from the ModelState. But i could't find their sources.
I need to implement this in my own temlate. Should i just check for a present value in ModelState and use it if it is set. Or may you suggest a better way?

I had a look at the HtmlHelpers (editors) and came up with this. In the example the model/ value is an integer:
var value = Model; //value according to the model
var fieldName = ViewData.TemplateInfo.GetFullHtmlFieldName("");
ModelState modelState;
if(ViewData.ModelState.TryGetValue(fieldName, out modelState) && modelState.Value != null)
{
value = (int?) modelState.Value.ConvertTo(typeof(int?), null);
}
Now the value contains the model value or the posted ModelState value (the attemptedValue).

Related

HTML Helper checking if the date is MinValue

Can I configure the HTML Helper to display String.Empty if my date is MinValue?
${Html.TextBoxFor(v=>v.Date)}
How can I do this?
Why don't you do it in your View?
Caution: Razor syntax
#if(v.Date != DateTime.MinValue)
{
Html.TextBoxFor(v => v.Date);
}
else {
//show whatever
}
There are a few similar questions like
Html.TextboxFor default value for Integer/Decimal to be Empty instead of 0
Display empty textbox using Html.TextBoxFor on a not-null property in an EF entity
If you have full control over the viewmodel, one option would be to make the date field nullable and have the accessor return null if the date is equal to DateTime.MinValue.
If you don't want to change your viewmodel, you can write your own html helper and put the logic to create an empty textbox there. That allows you to keep logic out of your view, where it's difficult to test.
Make your property nullable and it won't default to the min date. If that's a database field and you can't do that then you should create a ViewModel property that is and have it compare against the min value and return null in the get{}

Is this by design in MVC Model Binding?

A simple scenario that I've never seen before, but a colleague has just hit - MVC3
Create an action method MyAction(int myProperty = 0)
Create a model that has a property MyProperty
Pass an instance of this model to a strongly typed view, but set the property to 10 in code (don't use the query string parameter!)
In the view, Html.TextBoxFor(x => x.MyProperty)
This should render 10 in the text box.
Now call the action method MyAction?myProperty=8
Shouldn't this still render 10 in the text box?
I see that I can override the property discovered by the expression and assume this is because they are the same name (Query String parameter and model property). Eveything is then in the ViewData but one overrides the other.
Is this by design?
This is by design - ModelState is the highest priority value-provider for model properties, higher than even model itself. Without query string parameter, ModelState does not contain value for MyProperty, so framework uses model value.
You can use ModelState.Remove("MyProperty") to ensure using model value
If you look at the source code for Html.TextBoxFor you will see that if a value exists in ModelState then it will always use that value before any other.
string attemptedValue = (string)htmlHelper.GetModelStateValue(fullName, typeof(string));
tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(fullName, format) : valueParameter), isExplicitValue);
If the value is in ModelState, then it doesn't matter what you set in code.

How will model binder deals with Readonly & disabled

I have the following two items , one which is readonly:-
#Html.TextBoxFor(model => model.Technology.Tag, new
{ #readonly = "readonly" })
While the other is Disabled:-
#Html.DropDownListFor(model => model.Customer.NAME, ((IEnumerable<TMS.Models.AccountDefinition>)ViewBag.Customers).Select(option => new SelectListItem
{
Text = (option == null ? "None" : option.ORG_NAME),
Value = option.ORG_NAME.ToString(),
Selected = (Model != null) && (Model.Customer != null) & (option.ORG_NAME == Model.Customer.NAME)
}), "Choose...", new { disabled = "disabled" })
so will the asp.net mvc model binder bind the two items? , or it will ignore any read-only and any disabled fields ?
It will bind them, but as long as you have populated them with the correct data, that shouldn't matter. Also, you can have your code that maps these models to the entity, assuming you are using view models, just ignore the values in question. Assuming this is a standard HTTP Post from the form, HTTP will not post disabled or readonly fields, which means they will be null or the default value in the model, so you need to account for that.
If you want the binder to ignore these values, use TextBox and DropDownList and make sure they are not named the same as your properties. If you do not use 'For' you will need to add code in the view to set the values.
ReadOnly text fields get bound.
I've been trying to find a way around the unbound dropdownboxfor values. Here's what I came up with:
$('#xxx').change(function() {$(this).val(lastSel_xxx);});
var lastSel_xxx = $("#xxx option:selected").val();
Use the code above for each HTML element in your view (this will require you to document each ID output) and replace 'xxx' with the element name.
When a user selects another option besides the initial option selected, it reverts back to the original.
HOpe this helps!

ASP.NET MVC 3 HtmlHelper.Textbox Uses ModelState as a source for value?

I have a textbox that I am trimming the value of server side in the viewModel's setter and verifying that the trimmed value is present:
<Required()>
Public Property Name As String
Get
Return Me.mName
End Get
Set(value As String)
Me.mName = value.Trim()
End Set
End Property
In the controller, I check to see if the ModelState is invalid, and if it is, I show the view again:
If (Not ModelState.IsValid) Then
Return View("Locations", viewModel)
End If
In situations where the trimmed value is blank, I re-display the form to the user. At this point, the non-trimmed values are put into the textbox. I would much prefer the trimmed value.
I was looking through the MVC sourcecode, and I found that at one point in the InputHelper function (used by Html.Textbox(), located in InputExtensions.cs) the code was checking the ModelState for the value, and if it existed, replacing the controller-supplied value.
string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));
tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter), isExplicitValue);
I realize that I could fix this by clearing the ModelState in the controller, but I'm just wondering why the MVC Developers chose to have the values in the ModelState supersede the view model values.
This was designed this way because the assumption is if you are not redirecting to another view after a post .. Hence the post-redirect-get pattern, then there was ideally an error and the current posted values would be shown back to the user for them to fix hence why they are pulled from modelstate. You can ModelState.Clear to work with this but keep that design in mind.

What is the correct pattern to protect against NullReferenceExceptions in ASP.NET MVC

UPDATE
The issue was a syntax issue. #awrigley shows the correct way to write this in Razor.
The following works:
#if(Model.Thing.Prop != null)
{
Html.RenderPartial("SomePartialView", Model.Thing.Prop);
}
You have a requirement to show details for the top 1 Foo for a given Bar as an HTML table. How do you hide the empty table or show a "none found" message if that Foo is null?
For example. I'm getting a NullReferenceException on the following line because Model.Thing.Prop is null;
#{Html.RenderPartial("SomePartialView", Model.Thing.Prop);}
It is intentionally null, my Repository returns null instead of a empty Foo. But this is mildly beside the point, which is that given a null Model.Thing.Prop, I don't want to call the Html.RenderPartial.
Update
I've tried the following with no luck:
#if(Model.Thing.Prop != null)
{
#{Html.RenderPartial("SomePartialView", Model.Thing.Prop);}
}
Which results in Visual Studio telling me it's expecting a ; at line 1, column 1 and also that ; is an Invalid expression at line 1, column 1 (I'm guessing this is due to the pre-release status of MVC3), and if I hit the page in a browser I get
CS1501: No overload for method 'Write' takes 0 arguments
with the #Html.RenderPartial line highlighted.
I also tried
#if(Model.Thing.Prop != null)
{
<text>
#{Html.RenderPartial("SomePartialView", Model.Thing.Prop);}
</text>
}
but this results in a NullReferenceException from within my Partial View, which doesn't seem right. Model.Thing is definitely a valid Bar and Model.Thing.Prop is definitely a null Foo.
I presume you don't want to use...
#if (Model.Thing.Prop != null)
{Html.RenderPartial("SomePartialView", Model.Thing.Prop);}
...because you still want to render part of the partial view?
Oh, no. Have read your post properly.
I have no idea why the above code doesn't work for you.
EDIT:
Remember that in Razor, # can be used two ways:
To execute statements:
#{
MyStatement;
}
To evaluate an HtmlHelper, a class method, property or variable that returns a string of some form or other (eg, HtmlString, MvcHtmlString or string). For example:
#MyClass.MyStringProperty
Note that in case 2, no terminating semi colon is required.
1 and 2 indicate that if you have an htmlhelper that returns something other than a string (eg, void), then you have to call it as follows:
#{Html.MyHelperThatReturnsVoid;}
Whereas with an HtmlHelper that returns a string or HtmlString or MvcHtmlString, you can simply write:
#Html.MyHelperThatReturnsAString
For more detail, see the accepted answer to a question I asked:
Custom HtmlHelper that returns void problem
This is either a change in MVC3 or something odd.
MVC2 Behavior:
If Model.Thing is null, then yes it is a null ref exception.
If Model.Thing exists and Thing.Prop is null, then you'll pass a null reference to model. When you pass a null ref to model, parent model (the Model here) gets passed as the model to "SomePartialView" which would be expecting type Prop (unless Model and Prop are the same type).
Considering you're getting null ref, I'm going to assume that Model.Thing is null. In that case your only solution would be an #if statement. Passing null into a model ref simply doesn't work and I've torn some hair out over the behavior.
Again, this is MVC2 behavior, if this has change for MVC3 where you honestly can pass a null model, awesome!

Resources