Skip to content

Commit

Permalink
Merge pull request #67 from LiteracyFanatic/fix-removing-inputs
Browse files Browse the repository at this point in the history
Listen for removed inputs and unregister them
  • Loading branch information
haacked authored Aug 15, 2023
2 parents ab9ef9c + f1be447 commit 65e97ce
Showing 1 changed file with 80 additions and 6 deletions.
86 changes: 80 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,13 +518,17 @@ export class ValidationService {
/**
* Scans document for all validation message <span> generated by ASP.NET Core MVC, then tracks them.
*/
private scanMessages(root: ParentNode) {
private scanMessages(root: ParentNode, remove: boolean = false) {
/* If a validation span explicitly declares a form, we group the span with that form. */
let validationMessageElements = Array.from(root.querySelectorAll<HTMLElement>('span[form]'));
for (let span of validationMessageElements) {
let form = document.getElementById(span.getAttribute('form'));
if (form) {
this.pushValidationMessageSpan(form, span);
if (remove) {
this.removeValidationMessageSpan(form, span);
} else {
this.pushValidationMessageSpan(form, span);
}
}
}

Expand All @@ -540,7 +544,11 @@ export class ValidationService {
let validationMessageElements = Array.from(form.querySelectorAll<HTMLElement>('[data-valmsg-for]'));

for (let span of validationMessageElements) {
this.pushValidationMessageSpan(form, span);
if (remove) {
this.removeValidationMessageSpan(form, span);
} else {
this.pushValidationMessageSpan(form, span);
}
}
}
}
Expand All @@ -557,6 +565,22 @@ export class ValidationService {
}
}

private removeValidationMessageSpan(form: HTMLElement, span: HTMLElement) {
let formId = this.getElementUID(form);
let name = `${formId}:${span.getAttribute('data-valmsg-for')}`;
let spans = this.messageFor[name];
if (!spans) {
return;
}
let index = spans.indexOf(span);
if (index >= 0) {
spans.splice(index, 1);
}
else {
this.logger.log("Validation element for '%s' was already removed", name, span);
}
}

/**
* Given attribute map for an HTML input, returns the validation directives to be executed.
* @param attributes
Expand Down Expand Up @@ -649,7 +673,10 @@ export class ValidationService {

for (let i = 0; i < formInputUIDs.length; i++) {
let inputUID = formInputUIDs[i];
formValidators.push(this.validators[inputUID]);
const validator = this.validators[inputUID];
if (validator) {
formValidators.push(validator);
}
}

let tasks = formValidators.map(factory => factory());
Expand Down Expand Up @@ -895,6 +922,20 @@ export class ValidationService {
this.elementEvents[formUID] = cb;
}

private untrackFormInput(form: HTMLFormElement, inputUID: string) {
let formUID = this.getElementUID(form);
if (!this.formInputs[formUID]) {
return;
}
let indexToRemove = this.formInputs[formUID].indexOf(inputUID);
if (indexToRemove >= 0) {
this.formInputs[formUID].splice(indexToRemove, 1);
}
else {
this.logger.log("Form input for UID '%s' was already removed", inputUID);
}
}

/**
* Adds an input element to be managed and validated by the service.
* Triggers a debounced live validation when input value changes.
Expand Down Expand Up @@ -935,10 +976,22 @@ export class ValidationService {
this.elementEvents[uid] = cb;
}

removeInput(input: ValidatableElement) {
let uid = this.getElementUID(input);

delete this.summary[uid];
delete this.elementEvents[uid];
delete this.validators[uid];

if (input.form) {
this.untrackFormInput(input.form, uid);
}
}

/**
* Scans the entire document for input elements to be validated.
*/
private scanInputs(root: ParentNode) {
private scanInputs(root: ParentNode, remove: boolean = false) {
let inputs = Array.from(root.querySelectorAll<ValidatableElement>(validatableSelector('[data-val="true"]')));

// querySelectorAll does not include the root element itself.
Expand All @@ -949,7 +1002,12 @@ export class ValidationService {

for (let i = 0; i < inputs.length; i++) {
let input = inputs[i];
this.addInput(input);
if (remove) {
this.removeInput(input);
}
else {
this.addInput(input);
}
}
}

Expand Down Expand Up @@ -1216,6 +1274,15 @@ export class ValidationService {
this.scanInputs(root);
}

/**
* Scans the provided root element for any validation directives and removes behavior from them.
*/
remove(root: ParentNode) {
this.logger.log('Removing', root);
this.scanMessages(root, true);
this.scanInputs(root, true);
}

/**
* Watches the provided root element for mutations, and scans for new validation directives to attach behavior.
* @param root The root element to use, defaults to the document.documentElement.
Expand Down Expand Up @@ -1243,6 +1310,13 @@ export class ValidationService {
this.scan(node);
}
}
for (let i = 0; i < mutation.removedNodes.length; i++) {
let node = mutation.removedNodes[i];
this.logger.log('Removed node', node);
if (node instanceof HTMLElement) {
this.remove(node);
}
}
} else if (mutation.type === 'attributes') {
if (mutation.target instanceof HTMLElement) {
const oldValue = mutation.oldValue ?? '';
Expand Down

0 comments on commit 65e97ce

Please sign in to comment.