I am trying to figure out why my custom validation (i.e. the IsEmailValid
method) is not getting executed when I press the "submit" (i.e. Share) button below. The Required
one is getting executed fine but my custom validation attribute - IsEmailAttribute
is not getting called. Can someone please provide some directions.
That is my index.cshtml where my form is rendered via Html.RenderPartial
:
@using System.Security.Principal
@using DNAAnalysisCore.Resources
@model DNAAnalysisCore.ViewModels.WorkbookViewModel
@section BodyFill
{
<div id="shareFormContainer">
@{ Html.RenderPartial("_ShareView", new WorkbookShareModel());}
</div>
<table class="table">
<tbody>
@foreach (var workbook in Model.Workbooks)
{
<tr class="@trClassName">
<td>@Html.ActionLink(linkText, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td class="last-modified-date" title="Last Modified Date">@workbook.ModifiedDate.ToShortDateString()</td>
<td class="share">
<button title="Share" class="share-button" onclick='showSharingView("@workbook.Name", "@workbook.Id", "@Model.Id")'> </button>
</td>
</tr>
}
</tbody>
</table>
}
@section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
@await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
function showSharingView(title, workbookId, id) {
var isEmailAdapterName = 'isEmail';
$('#shareFormModal').modal("show");
// client-side email validation
$.validator.addMethod(isEmailAdapterName, IsEmailValid);
// register the 'isEmail' attribute with the 'unobtrusive' validation
$.validator.unobtrusive.adapters.add(isEmailAdapterName,
[],
function(options) {
// add a 'isEmail' rule to the options so that JQuery can find it when it's checking the rules collection
options.rules.isEmail = {};
// add a message for the 'isEmail' rule ('message' will have the 'ShareModel' attribute 'ErrorMessage' value)
options.messages[isEmailAdapterName] = options.message;
});
}
function hideDialog() {
var form = $("#partialform");
// only hide the dialog if the form is valid
if (form.valid()) {
$('#shareFormModal').modal("hide");
}
}
// Helper method that validates list of emails
function IsEmailValid(emailList, element, parameters) {
// EMAIL VALIDATION LOGIC HERE
}
</script>
}
That is my Model:
public class WorkbookShareModel
{
[HiddenInput]
public string Id { get; set; }
[HiddenInput]
public string WorkbookId { get; set; }
[HiddenInput]
public string Title { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources.BaseLanguage), ErrorMessageResourceName = "Email_Requirement_Warning")]
[IsEmailAttribute(ErrorMessageResourceType = typeof(Resources.BaseLanguage), ErrorMessageResourceName = "Email_Invalid_Message")]
public IList<string> Emails { get; set; }
}
public class IsEmailAttribute : ValidationAttribute, IClientModelValidator
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var share = (WorkbookShareModel)validationContext.ObjectInstance;
foreach (var email in share.Emails)
{
var convertedEmail = email.Trim().ToLower();
var match = Regex.Match(convertedEmail, Constants.EmailPattern);
// if one of the emails does not match the pattern, break out of the look and return the error message
if (!match.Success)
return new ValidationResult(ErrorMessage);
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-isEmail", errorMessage);
}
private void MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
return;
attributes.Add(key, value);
}
}
}
That is my form:
@using DNAAnalysisCore.Resources
@model DNAAnalysisCore.Models.WorkbookShareModel
<!-- Modal -->
<div onclick="activateShareButtons()" class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 id="dialogDisplayTitle" class="modal-title">THIS VALUE IS SET VIA JAVASCRIPT</h4>
</div>
@using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new { @id = "partialform" }))
{
<div class="modal-body">
<label>@BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="@BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
<input asp-for="Id" />
<input asp-for="WorkbookId"/>
</div>
<div class="modal-footer">
<button onclick="hideDialog()" type="submit" class="btn btn-primary">Share</button>
<button onclick="activateShareButtons()" id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
I managed to solve my problem by moving the $.validator.addMethod
and $.validator.unobtrusive.adapters.add
method calls outside the showSharingView
method. That worked and my custom error is shown properly. However, I am not sure why, though:
<script type="text/javascript">
var isEmailAdapterName = 'isEmail';
// client-side email validation
$.validator.addMethod(isEmailAdapterName, IsEmailValid);
// register the 'isEmail' attribute with the 'unobtrusive' validation
$.validator.unobtrusive.adapters.add(isEmailAdapterName,
[],
function(options) {
// add a 'isEmail' rule to the options so that JQuery can find it when it's checking the rules collection
options.rules[isEmailAdapterName] = {};
// add a message for the 'isEmail' rule ('message' will have the 'ShareModel' attribute 'ErrorMessage' value)
options.messages[isEmailAdapterName] = options.message;
});
function showSharingView(title, workbookId, id) {
$('.share-button').prop('disabled', true); // Disable all share buttons to avoid opening multiple dialogs
// reset the 'Emails' textarea
$('#Emails').val('');
// set the dialog's control values
$('#Title').val(title);
$('#Id').val(id);
$('#WorkbookId').val(workbookId);
// set the dialog's title
$('#dialogDisplayTitle').html("Share Workbook - " + title);
$('#shareFormModal').modal("show");
}