GordonS
GordonS

Reputation: 275

Add client side validation to dynamic form

I have a form (in a .Net Core 6 web application) where the input fields are created in code based on the content of an array of items. Each item defines what type of field / data it is and whether it is a mandatory field. I have code working that generates the form but I need to add the optional validation. I need it to work client side and also server side (to be tested with ModelState.IsValid).

The cshtml contains (some code removed for brevity):

@using Microsoft.AspNetCore.Mvc.TagHelpers
@inherits UmbracoViewPage<AdditionalInfoViewModel>


    <fieldset class="panel--shadow">
        <div class="panel theme--white">
        @for (var i = 0; i < Model.AdditionalInfo.Count; i++)
        {
            var item = Model.AdditionalInfo[i];
            var required = item.IsMandatory ? "required" : "";
            var fieldId = item.UdfName.Replace(' ', '-');

            <div class="form-group mb-4 @required">
                <input asp-for="@Model.AdditionalInfo[i].UdfName" type="hidden" />

                @{
                    var fieldName = Model.AdditionalInfo[i].TitleOverride;

                    switch (Model.AdditionalInfo[i].UdfDataType)
                    {
                        case "Text":
                            <label for="@fieldId" class="form-label mb-2">@fieldName</label>
                            <input class="form-control" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text"/>
                            break;


                        default:
                            <label for="@fieldId" class="form-label mb-2">@fieldName</label>
                            <input class="form-control d-inline-block w-auto" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text"/>
                            break;
                    }

                    if (Model.AdditionalInfo[i].IsMandatory)
                    {
                        <span class="field-validation-valid" data-valmsg-for="@fieldId" data-valmsg-replace="true"></span>
                    }

                }
            </div>

        }
        </div>

        <div class="panel theme--river text-center">
            <button class="btn btn-theme btn-primary js-additionalinfo-continue"
                    type="@Html.If(Model.HasNextStep, "button", "submit")">
                @if (!Model.HasNextStep)
                {
                    <i class="far fa-shopping-basket" aria-hidden="true"></i>
                }
                @Html.If(Model.HasNextStep, "Continue", "Add to basket")
                <i class="fa-regular fa-arrow-right" aria-hidden="true"></i>
            </button>
        </div>
    </fieldset>

The ViewModel is:

public class AdditionalInfoViewModel
{
    public AdditionalInfoViewModel()
    {
        Notice = string.Empty;
        AdditionalInfo = new List<AdditionalInfoListItem>();
    }

    public string Notice { get; set; }
    public bool HasNextStep { get; set; }

    public List<AdditionalInfoListItem>? AdditionalInfo { get; set; }
}

AdditionalInfoListItem is defined as:

public class AdditionalInfoListItem
{
    public string UdfName { get; set; }
    public string? TitleOverride { get; set; }
    public bool IsMandatory { get; set; }
    public string? UdfDataType { get; set;}
    public List<ListValueItem>? ListValues { get; set; }
    public string? UdfValue { get; set; }
}

public class ListValueItem
{
    public string ListValueName { get; set; }
    public bool Selected { get; set; }
}

So, if "item.IsMandatory" is true, I need to make the item input required and enable client & server side validation. If it is false, then I would like to make the item input not required (i.e. no validation required).

Because the ViewModel does not contain a specific set of fields, I cannot use the usual annotations on the ViewModel (at least, I assume I can't?).

The web app currently has "aspnet-validation.js" included, but do I need some other js for the client side validation?

Upvotes: 1

Views: 577

Answers (1)

Qiang Fu
Qiang Fu

Reputation: 8366

Adding attribute validation works in this scenario. But I did with <form method="post"></form> tag.
For example: add some requirement on UdfName.

public class AdditionalInfoListItem
    {
        
        public string UdfName { get; set; }
        public string? TitleOverride { get; set; }
        public bool IsMandatory { get; set; }
        public string? UdfDataType { get; set; }
        public List<ListValueItem>? ListValues { get; set; }
        [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
        [Required]
        public string? UdfValue { get; set; }
    }

Then put <form method="post"></form> to the whole fieldset. And add asp-validation-for span under the input you want to validate like this:

@using Microsoft.AspNetCore.Mvc.TagHelpers
@inherits UmbracoViewPage<AdditionalInfoViewModel>


<fieldset class="panel--shadow">
    <form method="post">
        <div class="panel theme--white">

            @for (var i = 0; i < Model.AdditionalInfo.Count; i++)
            {
                var item = Model.AdditionalInfo[i];
                var required = item.IsMandatory ? "required" : "";
                var fieldId = item.UdfName.Replace(' ', '-');

                <div class="form-group mb-4 @required">
                    <input asp-for="@Model.AdditionalInfo[i].UdfName" type="hidden" />

                    @{
                        var fieldName = Model.AdditionalInfo[i].TitleOverride;

                        switch (Model.AdditionalInfo[i].UdfDataType)
                        {
                            case "Text":
                                <label for="@fieldId" class="form-label mb-2">@fieldName</label>
                                if (Model.AdditionalInfo[i].IsMandatory)
                                {
                                    <input class="form-control" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text" data-val="true"/>
                                }
                                else
                                {
                                    <input class="form-control" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text" data-val="false"/>
                                }
                                <span asp-validation-for="@Model.AdditionalInfo[i].UdfValue" class="text-danger"></span>
                                break;


                            default:
                                <label for="@fieldId" class="form-label mb-2">@fieldName</label>
                                if (Model.AdditionalInfo[i].IsMandatory)
                                {
                                    <input class="form-control d-inline-block w-auto" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text" data-val="true"/>
                                }
                                else
                                {
                                    <input class="form-control d-inline-block w-auto" id="@fieldId" asp-for="@Model.AdditionalInfo[i].UdfValue" type="text" data-val="false"/>
                                }
                             
                                <span asp-validation-for="@Model.AdditionalInfo[i].UdfValue" class="text-danger"></span>
                                break;
                        }


                    }
                </div>

            }
        </div>

        <div class="panel theme--river text-center">
            <button class="btn btn-theme btn-primary js-additionalinfo-continue"
                    type="@Html.If(Model.HasNextStep, "button", "submit")">
                @if (!Model.HasNextStep)
                {
                    <i class="far fa-shopping-basket" aria-hidden="true"></i>
                }
                @Html.If(Model.HasNextStep, "Continue", "Add to basket")
                <i class="fa-regular fa-arrow-right" aria-hidden="true"></i>
            </button>
        </div>
    </form>
    
</fieldset>

There will be a validation alert for this input like this: enter image description here

Upvotes: 0

Related Questions