aspnet-client-validation icon indicating copy to clipboard operation
aspnet-client-validation copied to clipboard

Remote validator strange behavior

Open mind-ra opened this issue 3 years ago • 3 comments

I encountered a strange behavior for the Remote validator.

After the remote validator fire, regardless of the result, the form is posted even if the form is invalid.

It seem the preventDefault and the stopPropagation of the submit event is skipped, but I cannot understand how to fix it.

@page
@model IndexModel
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
    <form method="post">
        <label asp-for="Id"></label>
        <input asp-for="Id" />
        <span asp-validation-for="Id"></span>

        <label asp-for="Control"></label>
        <input asp-for="Control" />
        <span asp-validation-for="Control"></span>

        <button type="submit">Save</button>
    </form>

    <script src="aspnet-validation.js"></script>
    <script>
         const service = new aspnetValidation.ValidationService();
        service.bootstrap();
    </script>
</body>
</html>
public class IndexModel : PageModel
{
    [BindProperty]
    [Required]
    [Remote("CheckRemote", "Validations", HttpMethod = "Post")]
    public string Id { get; set; }
    [BindProperty]
    [Required]
    public string Control { get; set; }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            return BadRequest();
        }
        return Content("OK");
    }
}

[ApiController]
[Route("api/[controller]")]
public class ValidationsController : ControllerBase
{
    [HttpPost]
    [Route("check-remote")]
    public IActionResult CheckRemote()
    {
        bool isValid = false;
        return Ok(isValid);
    }
}

mind-ra avatar Feb 24 '22 08:02 mind-ra

Maybe is a timing problem.

The preventDefault and StopImmediatePropagation calls are inside the validate.then() promise. The submit event is not prevented, and while the validator is waiting for response from remote, the submit event is captured and processed and the page is posted.

...

var cb = function (e, callback) {
            var validate = _this.getFormValidationTask(formUID);
            if (!validate) {
                return;
            }
            validate.then(function (success) {
                var isProgrammaticValidate = !e;
                if (success) {
                    if (isProgrammaticValidate) {
                        callback(true);
                        return;
                    }
                    var validationEvent_1 = new CustomEvent('validation', {
                        detail: { valid: true }
                    });
                    form.dispatchEvent(validationEvent_1);
                    return;
                }
 //This here is inside a promise and cannot prevent in time the submit event
                if (e) {
                    e.preventDefault();
                    e.stopImmediatePropagation();
                }
                var validationEvent = new CustomEvent('validation', {
                    detail: { valid: false }
                });
                form.dispatchEvent(validationEvent);
                if (isProgrammaticValidate) {
                    callback(false);
                }
                else {
                    _this.focusFirstInvalid(form);
                }
            }).catch(function (error) {
                console.log(error);
            });
        };
...
...

var cb = function (e, callback) {
            var validate = _this.getFormValidationTask(formUID);
            if (!validate) {
                return;
            }

//Moved here to prevent the submit before validation
            if (e) {
                e.preventDefault();
                e.stopImmediatePropagation();
            }

            validate.then(function (success) {
                var isProgrammaticValidate = !e;
                if (success) {
                    if (isProgrammaticValidate) {
                        callback(true);
                        return;
                    }
                    var validationEvent_1 = new CustomEvent('validation', {
                        detail: { valid: true }
                    });
                    form.dispatchEvent(validationEvent_1);

// Had to resubmit the form here, but I don't know if it is correct. I don't know if I have to capture the validation CustomEvent and resubmit from there.
                    form.submit();

                    return;
                }
                
                var validationEvent = new CustomEvent('validation', {
                    detail: { valid: false }
                });
                form.dispatchEvent(validationEvent);
                if (isProgrammaticValidate) {
                    callback(false);
                }
                else {
                    _this.focusFirstInvalid(form);
                }
            }).catch(function (error) {
                console.log(error);
            });
        };

...

mind-ra avatar Feb 24 '22 11:02 mind-ra

Do you have an idea on how I should fix it?

haacked avatar Jun 04 '22 17:06 haacked

I moved the preventDefault outside the validate Promise, then resubmit the form if the validation has succeded.

If you look at my last comment all the fix i did is there.

mind-ra avatar Jun 16 '22 08:06 mind-ra