Skip to content

Commit

Permalink
Merge pull request #42 from haacked/haacked/41-validate-current-form
Browse files Browse the repository at this point in the history
Group validation messages by the form they belong to
  • Loading branch information
haacked authored Jun 6, 2023
2 parents 645a42e + 9f85ddb commit 5c8a8eb
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 63 deletions.
57 changes: 57 additions & 0 deletions Pages/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,63 @@
</form>
</fieldset>

<fieldset>
<legend>Form 2</legend>
<form method="post">
<div class="form-field">
<label asp-for="Id"></label>
<input asp-for="Id"/>
<span asp-validation-for="Id"></span>
</div>

<div class="form-field">
<label asp-for="Control"></label>
<input asp-for="Control"/>
<span asp-validation-for="Control"></span>
</div>

<button type="submit" class="btn">Save</button>
</form>
</fieldset>

<fieldset>
<legend>Form 3</legend>
<form method="post">
<div class="form-field">
<label asp-for="Input.SomeRequiredField"></label>
<input asp-for="Input.SomeRequiredField"/>
<span asp-validation-for="Input.SomeRequiredField"></span>
</div>
<button type="submit" class="btn">Save</button>
</form>
</fieldset>

<fieldset>
<legend>Form 4</legend>
<form method="post">
<div class="form-field">
<label asp-for="Input.SomeRequiredField"></label>
<input asp-for="Input.SomeRequiredField"/>
<span asp-validation-for="Input.SomeRequiredField"></span>
</div>
<button type="submit" class="btn">Save</button>
</form>
</fieldset>

<fieldset>
<legend>External Form</legend>
<div class="form-field">
<label asp-for="Input.SomeRequiredField" form="external"></label>
<input asp-for="Input.SomeRequiredField" form="external"/>
<span asp-validation-for="Input.SomeRequiredField" form="external"></span>
</div>
<button type="submit" class="btn">Save</button>
</fieldset>


<form id="external">

</form>

<script src="/dist/aspnet-validation.js"></script>
<script>
Expand Down
9 changes: 9 additions & 0 deletions Pages/Index.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,13 @@ public IActionResult OnPost()
StatusMessage = "Form submission successful";
return Page();
}

[BindProperty]
public InputModel Input { get; set; } = new();

public class InputModel
{
[Required]
public string? SomeRequiredField { get; set; }
}
}
82 changes: 54 additions & 28 deletions dist/aspnet-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,20 @@ var nullLogger = new (/** @class */ (function () {
}()))();
/**
* Resolves and returns the element referred by original element using ASP.NET selector logic.
* @param elementName
* @param element - The input to validate
* @param selector - Used to find the field. Ex. *.Password where * replaces whatever prefixes asp.net might add.
*/
function getRelativeFormElement(elementName, selector) {
function getRelativeFormElement(element, selector) {
// example elementName: Form.PasswordConfirm, Form.Email
// example selector (dafuq): *.Password, *.__RequestVerificationToken
// example result element name: Form.Password, __RequestVerificationToken
var realSelector = selector.substr(2); // Password, __RequestVerificationToken
var elementName = element.name;
var realSelector = selector.substring(2); // Password, __RequestVerificationToken
var objectName = '';
var dotLocation = elementName.lastIndexOf('.');
if (dotLocation > -1) {
// Form
objectName = elementName.substr(0, dotLocation);
objectName = elementName.substring(0, dotLocation);
// Form.Password
var relativeElementName = objectName + '.' + realSelector;
var relativeElement = document.getElementsByName(relativeElementName)[0];
Expand All @@ -177,7 +179,7 @@ function getRelativeFormElement(elementName, selector) {
}
}
// __RequestVerificationToken
return document.getElementsByName(realSelector)[0];
return element.form.querySelector("[name=".concat(realSelector, "]"));
}
/**
* Contains default implementations for ASP.NET Core MVC validation attributes.
Expand Down Expand Up @@ -218,7 +220,7 @@ var MvcValidationProviders = /** @class */ (function () {
if (!params.other) {
return true;
}
var otherElement = getRelativeFormElement(element.name, params.other);
var otherElement = getRelativeFormElement(element, params.other);
if (!otherElement) {
return true;
}
Expand Down Expand Up @@ -347,7 +349,7 @@ var MvcValidationProviders = /** @class */ (function () {
for (var _i = 0, fieldSelectors_1 = fieldSelectors; _i < fieldSelectors_1.length; _i++) {
var fieldSelector = fieldSelectors_1[_i];
var fieldName = fieldSelector.substr(2);
var fieldElement = getRelativeFormElement(element.name, fieldSelector);
var fieldElement = getRelativeFormElement(element, fieldSelector);
var hasValue = Boolean(fieldElement && fieldElement.value);
if (!hasValue) {
continue;
Expand Down Expand Up @@ -403,7 +405,7 @@ var MvcValidationProviders = /** @class */ (function () {
}());

/**
* Responsibles for managing the DOM elements and running the validation providers.
* Responsible for managing the DOM elements and running the validation providers.
*/
var ValidationService = /** @class */ (function () {
function ValidationService(logger) {
Expand Down Expand Up @@ -534,7 +536,7 @@ var ValidationService = /** @class */ (function () {
};
/**
* Returns true if the provided field is valid, and then calls the callback. The form will be validated before checking, unless prevalidate is set to false
* @param form
* @param field
* @param prevalidate
* @param callback
* @returns
Expand Down Expand Up @@ -623,24 +625,42 @@ var ValidationService = /** @class */ (function () {
* Scans document for all validation message <span> generated by ASP.NET Core MVC, then tracks them.
*/
ValidationService.prototype.scanMessages = function (root) {
var validationMessageElements = Array.from(root.querySelectorAll('[data-valmsg-for]'));
// querySelectorAll does not include the root element itself.
// we could use 'matches', but that's newer than querySelectorAll so we'll keep it simple and compatible.
if (root.hasAttribute("data-valmsg-for")) {
validationMessageElements.push(root);
}
for (var i = 0; i < validationMessageElements.length; i++) {
var e = validationMessageElements[i];
var name_1 = e.getAttribute('data-valmsg-for');
var spans = this.messageFor[name_1] || (this.messageFor[name_1] = []);
if (spans.indexOf(e) < 0) {
spans.push(e);
/* If a validation span explicitly declares a form, we group the span with that form. */
var validationMessageElements = Array.from(root.querySelectorAll('span[form]'));
for (var _i = 0, validationMessageElements_1 = validationMessageElements; _i < validationMessageElements_1.length; _i++) {
var span = validationMessageElements_1[_i];
var form = document.getElementById(span.getAttribute('form'));
if (form) {
this.pushValidationMessageSpan(form, span);
}
else {
this.logger.log("Validation element for '%s' is already tracked", name_1, e);
}
// Otherwise if a validation message span is inside a form, we group the span with the form it's inside.
var forms = Array.from(root.querySelectorAll('form'));
if (root.tagName === 'form') {
// querySelectorAll does not include the root element itself.
// we could use 'matches', but that's newer than querySelectorAll so we'll keep it simple and compatible.
forms.push(root);
}
for (var _a = 0, forms_1 = forms; _a < forms_1.length; _a++) {
var form = forms_1[_a];
var validationMessageElements_3 = Array.from(form.querySelectorAll('[data-valmsg-for]'));
for (var _b = 0, validationMessageElements_2 = validationMessageElements_3; _b < validationMessageElements_2.length; _b++) {
var span = validationMessageElements_2[_b];
this.pushValidationMessageSpan(form, span);
}
}
};
ValidationService.prototype.pushValidationMessageSpan = function (form, span) {
var formId = this.getElementUID(form);
var name = "".concat(formId, ":").concat(span.getAttribute('data-valmsg-for'));
var spans = this.messageFor[name] || (this.messageFor[name] = []);
if (spans.indexOf(span) < 0) {
spans.push(span);
}
else {
this.logger.log("Validation element for '%s' is already tracked", name, span);
}
};
/**
* Given attribute map for an HTML input, returns the validation directives to be executed.
* @param attributes
Expand Down Expand Up @@ -726,6 +746,12 @@ var ValidationService = /** @class */ (function () {
var tasks = formValidators.map(function (factory) { return factory(); });
return Promise.all(tasks).then(function (result) { return result.every(function (e) { return e; }); });
};
// Retrieves the validation span for the input.
ValidationService.prototype.getMessageFor = function (input) {
var formId = this.getElementUID(input.form);
var name = "".concat(formId, ":").concat(input.name);
return this.messageFor[name];
};
/**
* Returns true if the event triggering the form submission indicates we should validate the form.
* @param e
Expand Down Expand Up @@ -803,7 +829,7 @@ var ValidationService = /** @class */ (function () {
if (input.classList.contains(_this.ValidationInputValidCssClassName)) {
input.classList.remove(_this.ValidationInputValidCssClassName);
}
var spans = _this.messageFor[input.name];
var spans = _this.getMessageFor(input);
if (spans) {
for (var i = 0; i < spans.length; i++) {
spans[i].innerHTML = '';
Expand All @@ -824,8 +850,7 @@ var ValidationService = /** @class */ (function () {
var _this = this;
var uid = this.getElementUID(input);
var directives = this.parseDirectives(input.attributes);
var validate = this.createValidator(input, directives);
this.validators[uid] = validate;
this.validators[uid] = this.createValidator(input, directives);
if (input.form) {
this.trackFormInput(input.form, uid);
}
Expand Down Expand Up @@ -915,9 +940,10 @@ var ValidationService = /** @class */ (function () {
* @param message
*/
ValidationService.prototype.addError = function (input, message) {
var spans = this.messageFor[input.name];
var spans = this.getMessageFor(input);
if (spans) {
for (var i = 0; i < spans.length; i++) {
var span = spans[i];
spans[i].innerHTML = message;
this.swapClasses(spans[i], this.ValidationMessageCssClassName, this.ValidationMessageValidCssClassName);
}
Expand All @@ -937,7 +963,7 @@ var ValidationService = /** @class */ (function () {
* @param input
*/
ValidationService.prototype.removeError = function (input) {
var spans = this.messageFor[input.name];
var spans = this.getMessageFor(input);
if (spans) {
for (var i = 0; i < spans.length; i++) {
spans[i].innerHTML = '';
Expand Down
Loading

0 comments on commit 5c8a8eb

Please sign in to comment.