5NRF
5NRF

Reputation: 485

Dynamically add forms and have validation work

I have a situation where I have a ViewModel that has a number of fields (As an example):

public class myVM
{
     [Required(errorMessage="SomeMessage")]
     public string Name;

     [Required(errorMessage="SomeMessage")]
     public string Address;

     [Required(errorMessage="SomeMessage")]
     public string PhoneNumber;
}

In the real world its a bit more complicated.

The user is given a choice of what they are on the page to edit, Name Info, Address Info, Phone Number Info etc.

Each of those options corresponds to a partial view that is loaded with the form for each section.

The issue that I have is that if a user goes on and selects that they want to just edit the Name section, so only the name form is rendered they still get the validation messages for the Address and Phone numbers...

I know WHY this is happening, just not sure what the best way to get round it is?

UPDATE.

Noted that the above is unclear, these form parts (Partial views) are all loaded into one BIG form.

Something like this :

@using (Html.BeginForm())
{

    @Html.ValidationSummary(false)

    Html.RenderPartial("Partials/_AddressForm", Model.Address);
    Html.RenderPartial("Partials/_Phonenumber", Model.PhoneNumber);

    <input type="submit" value="SAVE"/>

}

The resulting form is posted back into one action which does a ModelState.IsValid - Which of course validates the WHOLE model.

Upvotes: 0

Views: 75

Answers (1)

D3vy
D3vy

Reputation: 986

In Webforms you would use Validation groups where you have a bunch of form controls that all need to be validated depending on different buttons being pressed.

You can achieve this with a single view model by implementing a custom validation annotation like this :

public class RequiredIfISaySo : ValidationAttribute//, IClientValidatable
{
    public RequiredIfISaySo(int RequiredReason)
    {
        _reqReason = RequiredReason;
    }

    private RequiredAttribute _reqAttribute = new RequiredAttribute();
    private int _reqReason;

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (_reqReason == 1)
        { 
            if (!_reqAttribute.IsValid(value))
            {
                return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
            }
        }

        return ValidationResult.Success;
    }
}

You can then annotate your model like this:

 [RequiredIfISaySo(3, ErrorMessage = "NAME REQUIRED")]
    public string Name { get; set; }

    [RequiredIfISaySo(2, ErrorMessage = "PHONE REQUIRED")]
    public string Phone { get; set; }

    [RequiredIfISaySo(1, ErrorMessage ="ADDRESS REQUIRED")]
    public string Address { get; set; }

I'd probably change that int to something easier to read, and change it so that you can pass an array rather than a single value (that way you can have overlap in the validation (ie validate anything where 1 or 2 is passed.

For bonus points you can also hook this up to the client side validation by implementing the (commented out) IClientValidatable interface.

Upvotes: 1

Related Questions