Reputation: 275
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
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:
Upvotes: 0