jeudi 19 août 2021

Using a custom required attribute to validate a list of checkboxes

I am wanting to validate a list of checkboxes by making sure that at least one is checked in my asp.net web mvc project without having to use javascript. The issue I am facing is that when I run the program it crashes and throws an exception stating:

System.InvalidCastException: 'Unable to cast object of type 'Plan.API.Models.ViewPhoneNumberInput' to type 'System.Collections.Generic.List`1[Plan.API.Models.ViewPhoneNumberInput]'.'

on the line of var viewModel = (List<ViewPhoneNumberInput>)validationContext.ObjectInstance;

Model class ViewPhoneNumberInput.cs:

public class ViewPhoneNumberInput
{
        [Required(ErrorMessage = "You did not enter your phone number! Please enter your phone number!")]
        public String PhoneNumber { get; set; }
        [RequiredCustom(ErrorMessage = "Please select at least one checkbox")]
        public List<Plans> plans { get; set; }
}

public class RequiredCustom : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
         var viewModel = (List<ViewPhoneNumberInput>)validationContext.ObjectInstance;

         foreach (var item in viewModel)
         {
             var x = item.plans;
             foreach (var plan in x)
             {
                 if (plan.IsSelected == false)
                 {
                     return new ValidationResult(ErrorMessage ?? "Please check one checkbox!");
                 }
             }
         }
      
        return ValidationResult.Success;
    }
}

my other model class Plans.cs:

public class Plans
{
    public int PlanId { get; set; }
    public string PlanName { get; set; }
    public bool IsSelected { get; set; }
}

and in my view controller I call the RequiredCustom class:

@model Plan.API.Models.ViewPhoneNumberInput

@{ ViewBag.Title = " "; }

<h2>Select Your Plan</h2>

@using (Html.BeginForm("ProcessingData", "Second"))
{
    for (int i = 0; i < Model.plans.Count; i++)
    {
        int counter = 0;

        @Html.CheckBoxFor(r => Model.plans[i].IsSelected)
        <label>  @Model.plans[i].PlanName</label><br />

        @Html.HiddenFor(h => @Model.plans[i].PlanId)
        @Html.HiddenFor(h => @Model.plans[i].PlanName)

        if (Model.plans[i].IsSelected)
        {
            counter++;
        }
        else if (counter < 1) // here if no boxes have been chosen it should display the error on screen
        {
            Plan.API.Models.RequiredCustom custom = new();

            custom.IsValid(Model.plans);

        }

    }



    <p>Phone Number</p>
    @Html.TextBoxFor(r => Model.PhoneNumber)
    <p>  @Html.ValidationMessageFor(r => Model.PhoneNumber) </p>

   
    <input id="Button" type="submit" value="Next" />

}

So I then removed the keyword List from var viewModel and changed the rest of the code to this:

public class ViewPhoneNumberInput { [Required(ErrorMessage = "You did not enter your phone number! Please enter your phone number!")] public String PhoneNumber { get; set; } [RequiredCustom(ErrorMessage = "Please select at least one checkbox")] public List plans { get; set; } }

public class RequiredCustom : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var viewModel = (ViewPhoneNumberInput)validationContext.ObjectInstance;

        foreach (var item in viewModel.plans)
        {
            if (item.IsSelected == false)
            {
                return new ValidationResult(ErrorMessage ?? "Please check on checkbox!");
            }
        }
      
        return ValidationResult.Success;
    }
}

but doing this for some odd reason viewModel.plans inside of the foreach() returned null:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

Plan.API.Models.ViewPhoneNumberInput.plans.get returned null.




Aucun commentaire:

Enregistrer un commentaire