1

I have a Parent Model that contains a property used by a Sub Model for pre-filling a field on the View. I would like to move the property into the Sub Model where it belongs, but another property's range attribute fails when i do this. Why is the range attribute failing validation when i have a property used only to be hidden on the EditorTemplate?

The Model looks like

public class ParentModel
{
    public SubModel subModel { get; set; }
}
public class SubModel
{
    public uint? DefaultValue { get; set; }

    [Required]
    [Range(1,100)]
    public uint RangedId { get; set;}

    public bool EnableRange { get; set; }
}

The View (EditorTemplate) Looks like

@model SubModel
@Html.HiddenFor(model => model.DefaultValue)
@Html.TextBoxFor(model => model.RangeId)
<script>
     $('#EnableRange').change(function() {
         if($('#EnableRange').val()){
             // remove the disabled attribute from the RangeId Field
         } else {
             // add the disabled attribute from the RangeId Field
         }
     }
</script>

The Controller Looks Like

public ActionResult Create(TViewModel model)
{
    try
    {
        if (ModelState.IsValid)
        {
            //Do Something Meaningful
        }
        //Redisplay the view
    }
}

With the DefaultValue property in the SubModel the RangeId's Range Attribute fires even when the RangeId is disabled on the form. This causes the ModelState.IsValid to be false. When I move the DefaultValue property up to the ParentModel the Range attribute for RangeId no longer fires (because the field is disable). Which causes the ModelState.IsValid to be true, because the RangeId is never evaluated for validation.

tereško
  • 58,060
  • 25
  • 98
  • 150
Mark
  • 145
  • 1
  • 7
  • It may help if you post the snippet of html that is rendered by the view. It's difficult to understand what you're attempting to do with your js. – wooters Jan 22 '15 at 18:23
  • Unclear what your doing here. If you disable a control, it does not post back, in which case `ModelState` would be invalid because the value of `RangedId` is `0` –  Jan 22 '15 at 21:16

1 Answers1

2

Whatever you think is happening is NOT happening. The server side Model.IsValid does not care anything about, nor is it directly affected by disabling the control on the client side (though it can be indirectly affected as we will see below). The validation will always occur if nested form fields are posted and nested objects have required properties.

More likely, the real issue here is that when you have DefaultValue in the child model, then when you submit the model to the parent, the model binder creates an instance of SubModel because it contains a value for DefaultValue. When you move it to the parent, and you disable the RangeId, there is no value to post and therefore no SubModel gets created, and thus no validation occurs.

Ie, my guess is that when you move DefaultValue to the parent, SubModel is null on postback, thus because there is no instance to validate, there is nothing to fail validation, particularly since you are not persisting the EnableRange value.

So you really have several issues. First, disabling a control on the client will not disable validation on the server. Second, if there are no nested form fields posted to the server, then no nested objects will be created and no validation will occur (so in that respect, validation can be disabled as a side-effect if you are very careful). Third, If you DO post some nested form fields but not others, validation then nested objects WILL get created and validation will occur on fields that are not posted because they were disabled.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Sounds like a good reason to use the [IValidatableObject](http://stackoverflow.com/questions/3400542/how-do-i-use-ivalidatableobject) interface. – Erik Philips Jan 22 '15 at 21:28
  • Agreed. It looks like fairly complicated logic. Server side logic could be duplicated in the IValidatableObject or create some [custom attributes](http://stackoverflow.com/questions/19726404/client-side-validation-in-custom-validation-attribute-asp-net-mvc-4) to consolidate the logic. – Erik Philips Jan 22 '15 at 21:33