IValidatableObject not firing upon form submission

310 views Asked by At

I'm new to .net and am attempting to write some slick custom validation for a CustomerInfo form that I'm submitting. To do this, I'm implementing IValidatableObject on the model class and following this template from microsoft's documentation.

However, no matter what, I cannot get this to trigger. At this point, I've removed any conditionals from the Validate method just to see if I can get it to fire on a submission. I should note that above the Validate method, I see that vsCode has a "0 References" note that seems suspicious. What might I be missing?

I have looked into a few similar posts here and here The first is not the same situation and I'm sufficiently new to the framework that I can't tell if the second is.

Below is the model to which I'm referring. This is the only file that I have modified to accommodate the IValidatableObject:

using CS_Test.Mappers;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.Json.Serialization;
using System.ComponentModel.DataAnnotations;

namespace CS_Test.Models
{
    public class CustomerInfo : IValidatableObject
    {
        public string ServiceType {get; set;}
        [DisplayName("First Name")]
        public string FirstName {get; set;}
        [DisplayName("Middle")]
        public string MiddleName {get; set;}
        [DisplayName("Last Name")]
        public string LastName {get; set;}
        public string BusinessName {get; set;}
        [Required]
        [StringLength(9, ErrorMessage = "Tax id must be 9 digits.")]
        [DisplayName("Tax Id")]
        public string TaxId {get; set;}
        [Required]
        [DisplayName("Security Password")]
        public string SecurityPassword {get; set;}
        [Required]
        [DisplayName("Date Of Birth")]
        public DateTime DateOfBirth {get; set;}
        public string auto_generate_loginField {get; set;}
        [Required]
        [RegularExpression(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", 
        ErrorMessage = "Not a valid email.")]
        [DisplayName("Email Address")]
        public string Email {get; set;}
        [Required]
        [StringLength(10, ErrorMessage = "Phone number must be 10 digits.")] 
        [DisplayName("Phone Number")]
        public string Phone {get; set;}
        [Required]
        [DisplayName("Address")]
        public string Address1Field {get; set;}
        [DisplayName("Address2")]
        public string Address2Field {get; set;}
        [Required]
        public string City {get; set;}
        [Required]
        public string State {get; set;}
        [Required]
        [DisplayName("Zip Code")]
        public string Zip {get; set;}

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            yield return new ValidationResult(
                "Test test test",
                new[] { nameof(FirstName)});
        }
    }
}

And (for what it's worth) here's the form that I'm submitting.

@model Models.CustomerInfo

<form asp-controller="ServiceOrder" asp-action="CreatePost" class="row g-3" id="customerInfoForm" method="post">
    <div class="form-check">
        <input class="form-check-input" type="radio" name="serviceType" id="residentialService" checked>
        <label class="form-check-label">Residential</label>
    </div>
    
    <div class="form-check">
        <input class="form-check-input" type="radio" name="serviceType" id="businessService">
        <label class="form-check-label">Business</label>
    </div>
    
    <div class="col-md-12 residentialOnly">
        <label class="requiredField">Name</label>
        <div class="row" id="personName">
            <div class="col-md-5">
                <input type="text" class="form-control" asp-for="FirstName" placeholder="First" required>
                <span class="text-danger" asp-validation-for="FirstName"></span>
            </div>
            <div class="col-md-2">
                <input type="text" class="form-control" asp-for="MiddleName" placeholder="Middle">
                <span class="text-danger" asp-validation-for="MiddleName"></span>
            </div>
            <div class="col-md-5">
                <input type="text" class="form-control" asp-for="LastName" placeholder="Last" required>
                <span class="text-danger" asp-validation-for="LastName"></span>
            </div>
        </div>
    </div>

    <div class="col-md-12 businessOnly">
        <label class="requiredField" asp-for="LastName">Business Name</label>
        <input type="text" class="form-control" asp-for="LastName" required>
        <span class="text-danger" asp-validation-for="LastName"></span>
    </div>
    
    <div class="col-md-12">
        <label class="requiredField" asp-for="SecurityPassword">Security Password</label>
        <input type="text" class="form-control" asp-for="SecurityPassword" required>
        <span class="text-danger" asp-validation-for="SecurityPassword"></span>
    </div>

    <div class="col-md-12">
        <label class="requiredField" asp-for="TaxId">Tax Id</label>
        <input type="text" class="form-control" asp-for="TaxId" required>
        <span class="text-danger" asp-validation-for="TaxId"></span>
    </div>

    <div class="col-md-12">
        <label class="requiredField" asp-for="DateOfBirth">Date of Birth</label>
        <input type="date" class="form-control" asp-for="DateOfBirth" required>
        <span class="text-danger" asp-validation-for="DateOfBirth"></span>
    </div>

    <div class="col-md-12">
        <label class="requiredField" asp-for="Email">Email</label>
        <input type="email" class="form-control" asp-for="Email" required>
        <span class="text-danger" asp-validation-for="Email"></span>
    </div>

    <div class="col-md-12">
        <label class="requiredField" asp-for="Phone">Phone</label>
        <input type="phone" class="form-control" asp-for="Phone" required>
        <span class="text-danger" asp-validation-for="Phone"></span>
    </div>

    <div class="col-12">
        <label class="form-label requiredField" asp-for="Address1Field">Address</label>
        <input type="text" class="form-control" asp-for="Address1Field" placeholder="1234 Main St" required>
        <span class="text-danger" asp-validation-for="Address1Field"></span>
    </div>
    
    <div class="col-12">
        <label class="form-label" asp-for="Address2Field">Address 2</label>
        <input type="text" class="form-control" asp-for="Address2Field" placeholder="Apartment, studio, or floor">
        <span class="text-danger" asp-validation-for="Address2Field"></span>
    </div>
    
    <div class="col-md-6">
        <label class="form-label requiredField" asp-for="City">City</label>
        <input type="text" class="form-control" asp-for="City" required>
        <span class="text-danger" asp-validation-for="City"></span>
    </div>

    <div class="col-md-4">
        <label class="form-label requiredField" asp-for="State">State</label>
        <div>
            <select id="inputCustomerState" class="form-select" style="width: 100%" asp-for="State" required>
            </select>
        </div>
        <span class="text-danger" asp-validation-for="State"></span>
    </div>

    <div class="col-md-2">
        <label class="form-label requiredField" asp-for="Zip">Zip</label>
        <input type="text" class="form-control" asp-for="Zip" required>
        <span class="text-danger" asp-validation-for="Zip"></span>
    </div>
    
</form>

<script>
    newForm = $("#customerInfoForm");
    $.validator.unobtrusive.parse(newForm);
</script>

By popular request, my controller methods

Used to render the form:

    public IActionResult Create()
    {
        var newOrderId = HttpContext.Session.Get<Guid>("NewOrderId");
        if (newOrderId == Guid.Empty) { newOrderId = NewOrderId(); } // if no order id was found, then create one
        ViewBag.NewOrderId = newOrderId;
        NewCustomerOrder order = GetNewCustomerOrder(newOrderId);
        return View(order);
    }

To Submit the form via ajax:

public IActionResult Review(CustomerInfo customerInfo)
        {
            // get the order from the cache
            var newOrderId = HttpContext.Session.Get<Guid>("NewOrderId");
            var order = GetNewCustomerOrder(newOrderId);    

            // update customer info on order
            order.CustomerInfo = customerInfo;

            // update the order in the cache
            var cacheEntryOptions = new MemoryCacheEntryOptions()
                    .SetAbsoluteExpiration(TimeSpan.FromDays(7));
            _cache.Set("NewOrderId" + newOrderId, order, cacheEntryOptions);

            return PartialView("_ReviewOrder", order);
        }

Here's the javascript/ajax.

$("#customerInfoForm").on("submit", function(e) {
    e.preventDefault(); 
    if ($("#customerInfoForm").valid() == false) { // check if the form is valid
        return false; 
    } else { // form is valid
        var customerInfo = $("#customerInfoForm").serializeArray();
        // hide other tabs
        $(".tab-pane").removeClass("active");
        $(".tab-pane").removeClass("show");
        $(".nav-link").removeClass("active");
        // show review tab
        $("#nav-tab-reviewOrder").addClass("active");
        $("#nav-tab-reviewOrder").addClass("show");
        $("#nav-link-reviewOrder").addClass("active");

        $("#tabColumn").addClass("w-100");
        $("#cartColumn").attr("hidden", true);
        $(".nav-link").attr("disabled", true);
        $(".nav-link").addClass("disabled");

        // get the review tab partial
        $.ajax({
            type: "POST",
            url: '/ServiceOrder/Review',
            data: customerInfo,
            datatype: "json",
            success: function (response) {
                $('#reviewOrderPartialView').html(response);  // response contains the html from the partial view

                // get the cart for review order
                $.ajax({
                    type: "POST",
                    url: '/Cart/Show',
                    datatype: "json",
                    success: function (response) {
                        $('#cartReviewPartialView').html(response);  // response contains the html from the partial view
                        // disable cart buttons
                        $(".cs-RemoveFromCartButton").attr("disabled", true);
                        $(".cs-RemoveFromCartButton").attr("hidden", true);
                    },
                    error: function () {
                        $('#cartReviewPartialView').html('Cart failed to load');  
                    }
                });
            },
            error: function () {
                $('#reviewOrderPartialView').html('Review Order failed to load');  
            }
        });
    }
})
0

There are 0 answers