GithubHelp home page GithubHelp logo

haacked / aspnet-client-validation Goto Github PK

View Code? Open in Web Editor NEW
206.0 206.0 17.0 1.19 MB

A client validation library for ASP.NET MVC that does not require jQuery

License: MIT License

Shell 0.05% PowerShell 0.02% CSS 1.31% TypeScript 60.32% C# 10.16% HTML 26.83% JavaScript 1.31%

aspnet-client-validation's Introduction

aspnet-client-validation's People

Contributors

analogrelay avatar andrewlock avatar andybutland avatar dahlbyk avatar daikoz avatar davidkarlsson avatar haacked avatar literacyfanatic avatar lonix1 avatar mind-ra avatar morganrowedhcw avatar ryanelian avatar skttl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

aspnet-client-validation's Issues

Dynamically added inputs are still being validated after removal

I have a multi-step form using Alpine.js where inputs can be added and removed in response to user actions. Using watch mode, the new inputs get picked up when added, but the library doesn't seem to properly clear them when they are removed. More specifically I am checking the form for validation errors using isValid before allowing the user to move to the next step, but isValid always returns false if any inputs have been removed from the form. I believe the issue is that the inputs still have entries in summary when isValid gets called. The issue also affects isFieldValid.

How to manually check if the form is valid?

Hi there,

Firstly a great library well done, I was wondering how do you go about checking if a form is valid from javascript?
Basically I have a need to only do something once the form is valid. With jquery validate it had the .valid() method, is there something similar?

Question

This and source fork doesn't say clearly one importing thing. It would be good to mention in your intro.

Can I just replace jquery.validate.min.js + jquery.validate.unobtrusive.min.js with this library and the validation would just work same way in ASP.NET Core application?

Meaning that this library is actually compatible and takes over or you have to code your own validation regardless?

Thank you.

Select elements don't validate properly

When calling isValid with invalid select elements the result is true instead of false. The select elements still get styled red indicating an error, but the summary object doesn't contain entries for them. This appears to be related to PR #57.

How to use with Razor Pages, i.e. using PageRemote attribute and not Remote attribute?

I am trying to get PageRemote and aspnet-client-validation working with a Razor Pages (.NET 8) project but it fails using wrong URL and not adhearing to the HttpMethod specified.
I have looked at the example here: https://github.com/haacked/aspnet-client-validation/blob/main/Pages/Demos/Checkboxes.cshtml.cs but it uses Remote attribute which is for MVC and not Razor Pages.

Model

public class ClientUserCreateModel {
    [Display(Name = "Email")]
    [Required]
    [BindProperty, PageRemote(
        ErrorMessage ="Email address already exists...", 
        HttpMethod ="post",
        PageHandler ="CheckEmail"
    )]
    public string Email { get; set; }
    ... // shortened for brevity
}

ClientUserCreate.cshtml

@page "/client/users/{userId:int?}"
@model ClientUserCreateModel

<form method="post">
    <input asp-for="Email" /> 
    <span asp-validation-for="Email"></span><br>
    <input type="submit"/>
</form>

@section Scripts { 
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aspnet-validation.min.js" integrity="sha256-VdWUgDcpAkvR4oohi4MJh2+X/poC68YBHE6zs0HKR+U=" crossorigin="anonymous"></script>
<script>
    const v = new aspnetValidation.ValidationService();
    v.bootstrap({ watch: true });
</script>
}

ClientUserCreate.cshtml.cs

public class ClientUserCreate: PageModel 
{
  public JsonResult OnPostCheckEmail(string email)
  {
      var existingEmails = new[] { "[email protected]", "[email protected]", "[email protected]" };
      var valid = !existingEmails.Contains(email);
      return new JsonResult(valid);
  }
}

If I change the email address in the form field to e.g. [email protected] and watch the Network I see a request like this:

https://localhost:44349/us/client/users/6767?handler=CheckEmail?Email=test%40example.com

  1. I specified post but instead it does get.
  2. The URL is not valid - there are two ? instead of one, and Email should be submitted as a form value instead of QueryString.

What am I doing wrong?

Missing key value pairs within FormCollection

Hello 👋

I have recently been attempting to transition away from jQuery and came across your solution as a replacement for validation. However, I have encountered an issue related to the posting back of key-value pairs as part of the FormCollection.

Interestingly, this issue seems to be browser specific. Everything operates as expected when using Safari, but the values do not appear to be included in the FormCollection when the same operation is performed in Chrome.

Let's consider a basic form that includes a few buttons as an example:

<form asp-controller="" asp-action="" method="post">
    <input asp-for=”FirstName”>
    <input asp-for=”LastName”>
    <button name=”button” value=”button1” type=”submit”>Button One</button>
    <button name=”button” value=”button2” type=”submit”>Button Two</button>
<form>

In this case, the 'FirstName' and 'LastName' fields get posted back as part of my model correctly, but the button and its value don't appear within the FormCollection.

As I said above, what's unusual is that this functions correctly in Safari, but not in Chrome.

If I comment out aspnet-client-validation script, the key and value do become part of the FormCollection for both browsers.

Am I missing something or is there a misstep in my process? I would greatly appreciate any pointers.

Thank you

isFieldValid unexpected behaviours

Hi,

I've got a use case for needing to validate a single field before sending some info to a rest api when clicking a button. It seems like isFieldValid should do this. However I'm having some issues with the way it works. If I provide the true argument to pre-validate the form it will then validate all other inputs on the form and show their error messages. This feels like a bad user experience as the user may not have got round to entering those fields yet.

Another issue is when you supply the prevalidate false argument then isFieldValid is assuming that some user interaction has happened and that there is an error message in the element cache for the form. I've made a fiddle that hopefully illustrates the issue.

https://jsfiddle.net/po75dqef/1/

Submit button's values missing from requests

With aspnet-client-validation in my forms, the submit button's value is missing from the request body.

<!-- button -->
<button type="submit" name="button" value="continue"></button>
<!-- input -->
<input type="submit" name="button" value="continue2" />

Disabling the validiation JavaScript makes the values appear in the request again.

The isValid method throws if the form does not contain any validation attributes

Hi,

I am using a function that calls ValidationService.isValid on every page on my site, but some pages do not have any validated fields. When called on a form with no fields that have validation, I would expect the return value to be true. Instead, the method throws on this line: var invalidFormInputUIDs = formInputUIDs.filter(...) with the error formInputUIDs is undefined.

razor pageremote type=post

In this.remote = function:

return new Promise(function (ok, reject)
{
var request = new XMLHttpRequest();
if (params.type === 'post') | not 'Post'. better yet: params.type.tolowercase

Removed inputs still get validated

When using the watch option, removed inputs still get validated. In my case, I did the following:

  1. Dynamically add inputs, leaving required fields blank
  2. Submit the form. As it is invalid, validation errors for the new inputs appear.
  3. Remove the invalid inputs.
  4. Submit the form. A JS error appears: TypeError: input.form is null. The input in question is not part of the form anymore (input.isConnected is false).

Option to ignore particular fields

I'm looking at using this library. One feature I believe is missing, is the ability to ignore particular fields. For example, using jquery.validate I would've been able to say:

$.validator.setDefaults({
    ignore: ':hidden:not(.validate-hidden)'
});

This allows me to conditionally specify which hidden fields I may validate. This can be expanded for additional cases too (such as the option of not validating disabled fields).

Manually validating a dynamically added form

After adding a form to the DOM (with watch enabled), a call to isValid() fails, when submitting the form:

window.validationService.isValid(form, false);

The error TypeError: formInputUIDs is undefined occurs at

let invalidFormInputUIDs = formInputUIDs.filter(uid => this.summary[uid]);

Variables:
grafik

Simplified form markup:

<form method="post" action="https://localhost:7222/item/disableitem/20/">
	<div class="validation-summary-valid" data-valmsg-summary="true"><ul><li style="display:none"></li></ul></div>
	<button type="submit">Yes</button>
	<input name="__RequestVerificationToken" type="hidden" value="xxx">
	<button type="button">No</button>
</form>

Disabling validation

For diagnostic and/or testing purposes we needed to disable the library once it's already loaded.

I tried this:

v.remove(v.options.root)

And this:

document.querySelectorAll('.my-form input:not([type="hidden"])').forEach(x => v.removeInput(x));

Both complete without error, but when I then interact with the form, and validation is expected to run, I get this console error:

Uncaught TypeError: i is not a function
  a http://localhost:5000/lib/aspnet-client-validation/dist/aspnet-validation.min.js:1
  setTimeout handler*i http://localhost:5000/lib/aspnet-client-validation/dist/aspnet-validation.min.js:1
  addInput http://localhost:5000/lib/aspnet-client-validation/dist/aspnet-validation.min.js:1
  scanInputs http://localhost:5000/lib/aspnet-client-validation/dist/aspnet-validation.min.js:1
  scan http://localhost:5000/lib/aspnet-client-validation/dist/aspnet-validation.min.js:1
  a http://localhost:5000/lib/aspnet-client-validation/dist/aspnet-validation.min.js:1
  bootstrap http://localhost:5000/lib/aspnet-client-validation/dist/aspnet-validation.min.js:1
  <anonymous> http://localhost:5000/signin:355

Are remove and removeInput intended to be usable by callers (in which case have I made a mistake in how I'm using them)? If not, is there some other way to disable the library once it's loaded, or is that impossible (and the only option is not to run it in the first place)?

Validation on submit not work

Hi. Thanks for creating this JavaScript library. So we can drop jquery.
There are 2 problems. (And sorry for my English)

  1. When submit form without writing anything it's not show validation message.
  2. When using validation with JavaScript code it's always return false.

Remote validator strange behavior

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);
    }
}

Do not validate immediately

In the original jQuery plugin it was possible to control when validation was activated. By default it would occur on each field's blur (demo).

This library performs validation immediately, i.e. as soon as the user starts typing in a field. That is not standard validation behaviour and leads to poor UX - the user is distracted by validation errors even before submitting the form.

Repro: I wanted to ensure it's not "just me", so:

  • I cloned the repo and ran the sample app
  • In the "Form 1" sample, type anything into the "Id (42)" field
  • Even before submitting, the validation errors are shown

I can identify these modes of activation:

  • current behaviour: validation is activated (for each field) immediately as the user types into a field
  • on form submit: validation is activated (for all fields) only when the user attempts to submit the form
  • on field blur (original plugin's behaviour): validation is activated (for each field) only after a field's first blur
  • on field blur with change: validation is activated (for each field) only after a field's blur and where the value was actually changed (to protect against user simply tabbing/clicking through fields without actually changing them)

Is any of this currently possible?

Purpose of debounce

I find the debounce default of 300ms to be quite slow, especially when changing from validating on input to on blur.

I changed it to 0 which serves my needs, but I'm unsure why that debounce is there, and whether I'm asking for trouble by changing it.

Why is there a debounce? Am I right in assuming it's specifically for the "validate on input" scenario?

(If so, I'll write up a little section for the docs...)

Missing Code From Release

Several functions are missing from the release zip file when compared to the code from the repo. For example, I can see the following functions missing when comparing the code from the aspnet-validation.js to the same file in the 0.5.1 release zip.

focusFirstInvalid
isValid
isFieldValid

You can see the differences if you compare the files.

Consider adding `novalidate` to forms to disable Constraints API

jQuery unobtrusive validation disables the default Constraints API behaviours. Without it, the constraints validation warnings interfere with the default validation messages:

image

The solution is to add novalidate="novalidate" to the containing <form> element. Is that something you think the library should do? It would make the library even more of a drop in replacement for unobtrusive validation?

Validation of a form with the same field name spills over into other forms.

If you have two forms with inputs that have the same name:

<form>
<input asp-for="MyInput />
<input type="submit" />
</form>

<form>
<input asp-for="MyInput />
<input type="submit" />
</form>

When you validate one form, it marks all the fields of all forms of the same name as invalid. That ain't good.

Validating list of radio buttons not working

I really like this library. I want to get rid of jQuery from our website.
However I'm having issues validating groups of radio buttons with this library. I have a list of <input type=radio asp-for="@MyModel">. The model has [Required]-attribute. By default .net will render data-val and data-val-required attribute for the first input. But when I post the form the radio buttons are not validated. The form is sent even if no radio button option is selected. If i use <input type=text> the validation works fine.

This is something i think works out of the box with jquery.validate.unobtrusive.

Another issue is if I'm using a Validation summary @Html.ValidationSummary() and the form is validated on the client it will remove the header rendered by @Html.ValidationSummary(). It seems that the script replaces the content of the validation summary element instead of just updating the content of the ul-list.

`bootstrap` should run immediately if `document.readyState === "interactive"`

The bootstrap method has some logic to defer loading until readyState === "complete":

bootstrap() {
this.addMvcProviders();
let document = window.document;
if(document.readyState === 'complete') {
this.scan();
}
else {
window.document.addEventListener('DOMContentLoaded', event => {
this.scan();
});
}
}

However, this can cause problems if bootstrap is called from within the DOMContentLoaded event. There are three readyState values:

  • loading - The document is loading
  • interactive - The document has finished loading and the document has been parsed but sub-resources such as scripts, images, stylesheets and frames are still loading.
  • complete - The document and all sub-resources have finished loading. The state indicates that the load event is about to fire.

When DOMContentLoaded fires, the current document.readyState is interactive (at least in Chromium-based Edge). Calling bootstrap here causes it to no-op because readyState !== "complete" AND DOMContentLoaded has already fired, so the handler it attaches will never be raised.

The simplest fix is probably to allow document.readyState === "interactive" to trigger an immediate scan. Also, it might be useful to make scan public. If it were public, I could just call that in my own DOMContentLoaded handler, since there's no reason for the defensive logic in bootstrap.

Validation breaks the formaction attribute

Suppose there is the following form:

<form action="/action_page.php">
  First name: <input type="text" name="fname"><br>
  Last name: <input type="text" name="lname"><br>
  <button type="submit">Submit</button><br>
  <button type="submit" formaction="/action_page2.php">Submit to another page</button>
  <button type="submit" name="param" value="val">Submit with param</button>
</form>

When using aspnet-client-validation, the form does not take into account the formaction attribute and the key/value passed to submit (if it was clicked). The only working solution is to add formnovalidate attribute to buttons.

<form action="/action_page.php">
  First name: <input type="text" name="fname"><br>
  Last name: <input type="text" name="lname"><br>
  <button type="submit">Submit</button><br>
  <button type="submit" formnovalidate="formnovalidate" formaction="/action_page2.php">Submit to another page</button>
  <button type="submit" formnovalidate="formnovalidate" name="param" value="val">Submit with param</button>
</form>

checkbox validation does not work

I have simple Razor page with a standard contact form on, where user is required to Accept Terms (checkbox/bool) before submitting.
The validation works for normal input type="text" but for the input="checkbox" no validation message is generated.

Script added to <body>:

<script src="https://cdn.jsdelivr.net/npm/aspnet-client-validation/dist/aspnet-validation.min.js" integrity="sha256-/PB6zFj7vHhLEgj9N93L5ls5YYCbREXwMGtQktTUDSI=" crossorigin="anonymous"></script>
<script>
    const v = new aspnetValidation.ValidationService();
    v.bootstrap({ watch: true });
</script>

ContactFormModel.cs

public class ContactFormModel
{
    [Required]
    public string Name { get; set; }        
    [Required]
    public string Email { get; set; }
    [Required]
    public string Message { get; set; }
    [Required] 
    public bool AcceptTerms { get; set; }

    public string Phone { get; set; }
}

Razor page .cshtml implementation of form:

@model ContactFormModel

<form asp-page="/Home/Contact" class="row g-3">
    <div class="mb-1">
        <label asp-for="Name" class="control-label"></label> * <span asp-validation-for="Name"></span>
        <input asp-for="Name" autofocus class="form-control"/>
    </div>
    <div class="col-auto">
        <label asp-for="Phone" class="control-label"></label> <span asp-validation-for="Phone"></span>
        <input asp-for="Phone" class="form-control"/>
    </div>
    <div class="mb-1">
        <label asp-for="Email" class="control-label"></label> * <span asp-validation-for="Email"></span>
        <input asp-for="Email" class="form-control"/>
    </div>
    <div class="mb-1">
        <label asp-for="Message" class="control-label"></label> * <span asp-validation-for="Message"></span>
        <textarea asp-for="Message" class="form-control"></textarea>
    </div>
    <div class="mb-1">
        <label asp-for="AcceptTerms" class="control-label">
        <input asp-for="AcceptTerms" class="form-check-input"/>Please accept terms</label> * 
        <span asp-validation-for="AcceptTerms"></span>
    </div>
    <div class="mb-1">
        <input type="submit" id="btnSubmit" value="@Res.Send" class="btn btn-primary text-white"/>
    </div>
</form>

Result when trying to submit the form:
contact-form

The generated code by ASP.NET:

<div class="mb-1">
<label class="control-label" for="AcceptTerms">
<input class="form-check-input" type="checkbox" data-val="true" data-val-required="The AcceptTerms field is required." id="AcceptTerms" name="AcceptTerms" value="true" /> Please accept terms
</label> * <span class="field-validation-valid" data-valmsg-for="AcceptTerms" data-valmsg-replace="true"></span>
 </div>
..
<input name="AcceptTerms" type="hidden" value="false" />

If anyone can point me in the right direction, I would be happy. The (https://github.com/haacked/aspnet-client-validation/blob/main/Pages/Demos/Checkboxes.cshtml) does not mention single checkbox validation to my knowledge

"Required" validation on optional checkboxes

Optional checkboxes trigger validation errors when there is a second input with the same name. .NET adds hidden inputs to optional checkboxes.

<input type="checkbox" class="form-check-input input-validation-error"
       data-val="true" data-val-required="The Type field is required." 
       id="Items_0__Type" name="Items[0].Type" value="true" disabled="disabled">
<label class="form-check-label" for="Items_0__Type">Type</label>
<span class="field-validation-error" data-valmsg-for="Items[0].Type" 
      data-valmsg-replace="true">The Type field is required.</span>

<!-- hidden input -->
<input name="Items[0].Type" type="hidden" value="false">

Not sure if this is a bug or if I'm just using this the wrong way.

Update:

Seems to be connected to the restriction on the type attribute at https://github.com/haacked/aspnet-client-validation/blob/3bdd41f1d10d8436155dbf0398113bf6981eab54/src/index.ts#LL131C1-L131C1

Initial isFieldValid returns true when field is empty and uses required validator.

On calling v.isFieldValid and passing an element it appears to use the cached version even though it has not previously run.

From the code for isFieldValid I can see the validation is set up to run on the debounce timeout, which I have set to 0 on bootstrap.

This results in isFieldValid returning true even though it has not called the validator. The validator fires after the function returns and evaluates to false. There is no async code.

I'm going to work around this by using the callback.

Is it an error that the state is returned as true when it has not been through a validator? From the docs on this function it's not obvious that the return state is cached.

Would you consider an alternative (which I'll help with) if the debounce is 0, calling the validator and returning the state immediately?
That might still make the return value unreliable if debounce is not 0 though.

Basic idea of what I am doing is below;

<input type="text" data-val="true" data-val-required="Please enter."id="ElementX" name="ElementX" value="" />

// Bootstrap was set with v.debounce = 0;

// On a button click, check if empty field is valid.
let isValid = v.isFieldValid(element));

// Returns true

Thank you

Ryan

[Bug] Checkbox validation does not work (verified in demo app)

My model has a bool bound to a checkbox. It's not required, so I should be able to submit the form without checking it. But validation fails for the checkbox and I can't submit.

I tried the demo, and it also fails.

The demo has this:

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

    // ...

    public class InputModel
    {
        [Remote("CheckboxRemote", "Validations", HttpMethod = "Post")]
        public bool IsChecked { get; set; }
    }

When trying to submit the form on the http://localhost:5000/Demos/Checkboxes page:

NET renders a checkbox and a hidden input ... it is unchecked. Unchecking it should not fail validation.

'IsChecked' is invalid. This is not checked

One can easily verify this repro, just run the demo, go to that page and click submit.

I think it's a bug.

Consider adding highlight/unhighlight events

I would love to replace our jQuery ASP.NET validation with this, but I can't because it's lacking the highlight and unhighlight events that we rely upon.

They're part of jQuery Validation rather than Microsoft's jQuery Unobtrusive Validation, and they let you run extra code at the time a field becomes valid or invalid.

Our use case for this is that we need error messages to be replicated in an Error summary similar to ASP.NET validation summary as well as next to the field, and we need to prepend "Error: " to the page title when there is an error. These changes make error messages more accessible to assistive technology users.

Would you consider adding support for them?

Should not validate disabled fields

The jQuery Validate plugin does not validate fields with the disabled attribute. However this library does.

If this library's purpose is to be a drop-in replacement, then one could argue this is a bug.

[Docs] Changing css class names

Apparently it's possible to change the default css class names, so we could use this library with bootstrap (for example).

But I can't find mention in the README, and haven't been able to figure it out.

Does someone know how to use the feature?

isFieldValid not working as expected if prevalidate is false

Hi there,

First of all, thank you for taking on the maintenance role of the library after Ryan. I've just started using the library so if I'm doing something wrong, please point me in the right direction.

Otherwise I've created a codepen repo with the problem and this is essentially the same problem as #28 I believe.

When I try to validate a specific field with prevalidate set to false in order to trigger an API call, the callback is never triggered and the result of isFieldValid appears to lag behind by one call.

i.e. if the last 7 results were false:

  • you'll get true on first call (this is coming from default state of control while its in pristine state)
  • you'll get false up-to the 8th call (1+7)
  • true on the 9th call (1+8 even though the field was valid on the 8th call).
  • lets say now the input was actually invalid on the 9th call, it'll take the 10th call to actually get that result.

I hope you can see now what I mean by "lagging behind by one call".

The behaviour only happens with pre-validate set to false as far as I can tell and behaves as expected when working with the entire form. Of course, that leaves a pretty bad impression when the entire form says its required, even though the user hasn't even gotten to rest of the form yet.

Rescanning dynamic inputs does not detect that they are new

I have a form with radio buttons. The buttons have validation on them to ensure one is always selected.

When those buttons are changed / checked, they are dynamically reloaded from the server with new text but the same name / id.

On trying to run validation I hit an error (element.form is null) as the element that is held within aspnet-client-validation no longer has a form attached. The element on the page does though. A longer trace for the error is below.

I tried doing a v.scan(form) as suggested to refresh the lib with the new inputs, but it seems it thinks it already has them as I get lots of 'already tracked' messages such as;

  • Validation element for 'xxxxx' is already tracked
  • Form input for UID 'yyyyy' is already tracked

Perhaps I need to remove the fields first, so I tried variations such as;

v.remove()
v.scan(form,true);

Followed by another v.scan(), but this fails with the following. I suspect it is because the validators are still there but the elements are not.

Uncaught (in promise) TypeError: root is undefined
    scanMessages aspnet-validation.js:742
    remove aspnet-validation.js:1364

Initial error without attempting to scan or remove;

Validation error TypeError: element.form is null
    required aspnet-validation.js:216
    ValidationService aspnet-validation.js:1254
    step aspnet-validation.js:142
    verb aspnet-validation.js:123
    __awaiter aspnet-validation.js:117
    __awaiter aspnet-validation.js:113
    ValidationService aspnet-validation.js:1230
    tasks aspnet-validation.js:892
    getFormValidationTask aspnet-validation.js:892
    cb aspnet-validation.js:949
    trackFormInput aspnet-validation.js:988
    addInput aspnet-validation.js:1050
    scanInputs aspnet-validation.js:1103
    scan aspnet-validation.js:1357
    init aspnet-validation.js:1336
    bootstrap aspnet-validation.js:1344
    Init validation-bootstrap.ts:27
    <anonymous> validation-bootstrap.ts:32

I can work around this by using { watch: true } on the bootstrap. Which is great, but how should I be doing this if I do it manually?

I can create a repro if needed. If it is decided it is an edge case and you'd like me to submit a PR, I can do that. I just need some direction as I'm not sure of the codebase.

Thank you,

Ryan

0.5.3 - isFieldValid or validateForm causing error

Hi there,

I can't seem to get the isFieldValid or validateForm working properly

v.isFieldValid(document.getElementById('Name')) OR
v.validateForm(document.getElementById('form-trade'));

Uncaught TypeError: Cannot read properties of null (reading 'submitter')
    at e.shouldValidate (index.ts:631:31)
    at i (index.ts:654:23)
    at e.validateForm (index.ts:566:13)
    at e.isFieldValid (index.ts:617:22)
    at <anonymous>:1:3

If I use the false parameter it always returns false

v.isFieldValid(document.getElementById('Name'), false )

Am I doing something wrong?

UPDATE It seems to be happening on on v0.5.3. I tested with 0.5.1 for validateForm and it's ok.
My guess is, it has to do with the noformvalidate changes made between 0.5.1 and 0.5.3

UPDATE 2
Found the issue. Need to check if e is null. I'll see if I can write a patch for this

 e.prototype.shouldValidate = function(e) {
                    return !(e.submitter && e.submitter.formNoValidate)
}

Calling `validateForm` submits the form in Firefox

I have a wizard style form with multiple sections where I call validateForm before the user tries to move to the next section. In Chrome this works as desired, but in Firefox it submits the form instead making it impossible to move beyond the first section.

Starting from formValidationEvent which is really cb the value undefined is passed for the SubmitEvent which causes it to bypass the call to preValidate which has a comment indicating that it is meant to prevent submission. The flow then progresses from handleValidated to submitValidForm. In Chrome it seems like the submission is actually prevented by accident; the line var submitter = submitEvent.submitter; throws an exception due to submitEvent being undefined. Firefox never gets past the call to form.dispatchEvent(newEvent) at which point it submits the form.

Why am i getting a client side validation for boolean field in a collection list ?

Im getting the following client side error when clicking submit button :

image

viewmodel

    public class AddPartsViewModel
    {
        public AddPartsViewModel()
        {
            PartSelections = new List<PartSelectionViewModel>();
        }

        public EditTypeViewModel Type { get; set; }
        public List<PartSelectionViewModel> PartSelections { get; set; }
    }

    public class PartSelectionViewModel
    {
        public string PartName { get; set; }
        public string PartDisplayName { get; set; }
        public string PartDescription { get; set; }
        public bool IsSelected { get; set; }
    }

view


@model AddPartsViewModel
@{
    int i = 0;
}
<zone Name="Title"><h5>@T["Add Parts To \"{0}\"", Model.Type.DisplayName]</h5></zone>

@T["Choose the Parts to add to this Content Type."]

<form asp-action="AddPartsTo">
    @Html.ValidationSummary()
    <div class="mb-3">
        <ul class="list-group">
            @foreach (var partSelection in Model.PartSelections)
            {
                <li class="list-group-item">
                    <div class="form-check">
                        <input type="checkbox" class="form-check-input" asp-for="PartSelections[i].IsSelected">
                        <label class="form-check-label" asp-for="PartSelections[i].IsSelected">@partSelection.PartDisplayName</label>
                        <span class="form-text">@partSelection.PartDescription</span>
                    </div>
                    <input asp-for="PartSelections[i].IsSelected" type="hidden" />
                    <input asp-for="PartSelections[i].PartName" type="hidden" />
                </li>
                i++;
            }
        </ul>
    </div>
    <div class="mb-3">
        <button class="btn btn-primary save" type="submit">@T["Save"]</button>
        <a class="btn btn-secondary cancel" role="button" asp-route-action="Edit" asp-route-id="@Model.Type.Name">@T["Cancel"]</a>
    </div>
</form>


Form e.preventDefault() broken in Firefox (0.8.3-0.8.13)

Hi there,

It seems any code such as the following in Firefox breaks.
Chrome / Edge seem ok, but Firefox doesn't work

document.getElementById('my-form').addEventListener('submit', function (e) { e.preventDefault(); alert('This doesn't get called because the page navigates away'); });
The page just navigates away without being stopped

I tried various versions of the library and v0.8.1 is the last working version
0.8.3-0.8.13 BROKEN

@dahlbyk I noticed you made some changes during 0.8.3 release anything you can think of that would stop it form working in Firefox?

I suspect an issue with this part of the code
v0.8.1...v0.8.3#diff-a2a171449d862fe29692ce031981047d7ab755ae7f84c707aef80701b3ea0c80R728

image

Any help would be much appreciated

Using watch, validation message spans aren't added to the register if new fields are added to form

The problem is that the root passed to ValidationService.prototype.scanMessages = function (root, remove) is the new element detected, not the form. scanMessages checks for a form and registers the message under the form, but it only walks down the DOM, so if the new element is within the form no form is found and the message is not registered.

In my case I can work round this by adding form attributes to the message spans before adding them to the DOM, but that probably isn't always going to be possible.

Probably need to walk up the DOM until finding a form if the root isn't a form and doesn't contain any forms. Only if that doesn't find a form (in which case the new element detected wasn't relevant) should the function return without searching for message spans to register.

[Q] Reparse form without using `watch:true` MutationObserver

The docs show how to validate dynamically-modified forms with a MutationObserver via v.bootstrap({ watch: true }).

But that is a little heavy when I know exactly when and how new items are added to the form.

I want to replace jquery-validate where that is done by reparsing the form. Just out of interest, it's done like this:

$(form).removeData("validator").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse(form);

I'm sure this library already has this built-in, but it doesn't seem to be documented.

Does someone know how to do this?

Thanks!

Custom validator for "differs to"

@haacked, @dahlbyk

I needed a custom validator that is the opposite of [Compare], i.e. validates that two inputs differ. My use case was a password change form: new and old passwords must differ. Was very tricky to get it working on both server and client sides.

Posting my solution here in case it helps someone else (and for my own future reference! 😆).

DiffersToAttribute.cs

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class DiffersToAttribute : ValidationAttribute, IClientModelValidator
{

  // based on
  // https://github.com/haacked/aspnet-client-validation/blob/062c8433f6c696bc41cd5d6811c840905c63bc9c/README.MD#adding-custom-validation
  // https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-7.0#custom-client-side-validation
  // https://github.com/dotnet/runtime/blob/dff486f2d78d3f932d0f9bfa38043f85e358fb8c/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CompareAttribute.cs
  // https://github.com/dotnet/aspnetcore/blob/d0ca5a8d20ac50a33d5451e998a5d411a810c8d7/src/Mvc/Mvc.DataAnnotations/src/CompareAttributeAdapter.cs

  private const string _errorMessage = "'{0}' and '{1}' must differ.";
  public override bool RequiresValidationContext => true;
  public string OtherProperty { get; }
  public string? OtherPropertyDisplayName { get; internal set; }

  public DiffersToAttribute(string otherProperty) : base(_errorMessage)
  {
    if (string.IsNullOrWhiteSpace(otherProperty)) throw new ArgumentNullException(nameof(otherProperty));
    OtherProperty = otherProperty;
  }

  public override string FormatErrorMessage(string name) =>
    string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, OtherPropertyDisplayName ?? OtherProperty);

  protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
  {
    var otherPropertyInfo = validationContext.ObjectType.GetRuntimeProperty(OtherProperty);

    if (otherPropertyInfo == null)
      return new ValidationResult(string.Format("Could not find a property named {0}.", OtherProperty));
    if (otherPropertyInfo.GetIndexParameters().Length > 0)
      throw new ArgumentException(string.Format("The property {0}.{1} could not be found.", validationContext.ObjectType.FullName, OtherProperty));

    var otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);

    if (Equals(value, otherPropertyValue))
    {
      OtherPropertyDisplayName ??= GetDisplayNameForProperty(otherPropertyInfo);
      var memberNames = validationContext.MemberName != null
         ? new[] { validationContext.MemberName }
         : null;
      return new ValidationResult(FormatErrorMessage(validationContext.MemberName ?? validationContext.DisplayName), memberNames);
    }

    return null;
  }

  private string? GetDisplayNameForProperty(PropertyInfo property)
  {
    var attributes = CustomAttributeExtensions.GetCustomAttributes(property, true);
    foreach (Attribute attribute in attributes)
    {
      if (attribute is DisplayAttribute display)
        return display.GetName();
    }
    return OtherProperty;
  }

  public void AddValidation(ClientModelValidationContext context)
  {
    ArgumentNullException.ThrowIfNull(context, nameof(context));
    MergeAttribute(context.Attributes, "data-val", "true");
    MergeAttribute(context.Attributes, "data-val-differsto", FormatErrorMessage(context.ModelMetadata.Name!));        // null forgiving: false positive as must be non-null for property
    MergeAttribute(context.Attributes, "data-val-differsto-other", $"*.{OtherProperty}");
    // allows other property to be nested or not, e.g. `FooModel.User.Name` and `FooModel.UserName`, respectively; https://github.com/dotnet/aspnetcore/blob/d0ca5a8d20ac50a33d5451e998a5d411a810c8d7/src/Mvc/Mvc.DataAnnotations/src/CompareAttributeAdapter.cs#L19
  }

  private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
  {
    if (attributes.ContainsKey(key)) return false;
    attributes.Add(key, value);
    return true;
  }

}

Add to js init scripts:

function getModelPrefix(fieldName) {                           // https://github.com/aspnet/jquery-validation-unobtrusive/blob/a5b50566f8b839177bc7733d67be3a37bca400ff/src/jquery.validate.unobtrusive.js#L44
  return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
}
function appendModelPrefix(value, prefix) {                    // https://github.com/aspnet/jquery-validation-unobtrusive/blob/a5b50566f8b839177bc7733d67be3a37bca400ff/src/jquery.validate.unobtrusive.js#L48
  return value.indexOf("*.") === 0
    ? value.replace("*.", prefix)
    : value;
}

const v = new aspnetValidation.ValidationService();
v.addProvider('differsto', (value, element, params) => {
  if (!value) return true;
  let prefix       = getModelPrefix(element.name);
  let otherName    = appendModelPrefix(params.other, prefix);
  let otherElement = Array.from(element.form.querySelectorAll('input')).filter(x => x.name === otherName)[0];
  let otherValue   = otherElement.value;
  return value !== otherValue;
});
v.bootstrap();

Example:

[Required]
[PasswordPropertyText]
[DataType(DataType.Password)]
public string PasswordOld { get; init; }

[Required]
[DiffersTo(nameof(PasswordOld), ErrorMessage = "Passwords must differ")]
[PasswordPropertyText]
[DataType(DataType.Password)]
public string PasswordNew { get; init; }

If you ever decide to enable the wiki repo, it would be nice to have a page where we can aggregate our custom validators.

Please close this issue once you've seen it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.