Giter Club home page Giter Club logo

iodine's People

Contributors

davidadrielsolutions avatar ellie-me avatar enaah avatar helloiampau avatar mattkingshott avatar mraheelkhan avatar periclestheo avatar roncallyt avatar simotod avatar unleashedmyself avatar wenmin92 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

iodine's Issues

Custom Error Messages Keys

This may or may not be an issue, but it's not how I was expecting it to behave!

When adding custom error messages (specifically in Single Item Checks):

a) you must provide custom messages for all validation rules.

The following returns 'undefined' for the error key of the numeric validation as there is no custom message.

Iodine.assert(field.value, ['required', 'numeric', 'minLength:2'], { 'required' : 'required.', 'minLength:2' : 'minLength.' });

Returned Object:
Object { valid: false, rule: "numeric", error: undefined }

I was expecting the default message back. It would seem it might make more sense to allow the overriding of just one or two validation messages and fall back to the default if one is not provided.

b) when using params in a validation the key for the custom error message must include the parameter.

The following returns 'undefined' for the error key of the minLength validation as it is looking for minLength:2 in the messages.

Iodine.assert(field.value, ['required', 'minLength:2'], { 'required' : 'required.', 'minLength' : 'minLength.' });

Returned Object:
Object { valid: false, rule: "minLength:2", error: undefined }

By specifying the parameter as the message key you get the correct message back:

Iodine.assert(field.value, ['required', 'minLength:2'], { 'required' : 'required.', 'minLength:2' : 'minLength.' });

I was expecting it to use the validation rule key as the message key and not to include the parameters as it is unlikely you would use two minLength rules on one field!

Obviously, both of these can be worked around, I just wasn't sure if it was by design. If it is by design can we add a small comment to the docs?

email/numeric and maxLenght doesn't work

['required', 'email', 'maxLength:199']

['required', 'maxLength:199', 'numeric']

If i put email/numeric and maxLength in the rules its not working...

Can we get a fast fix for this?

undefined is not an object in custom rule when value containing special characters

Hey there,

I'm running into an issue when trying to validate an input using custom rules.
I get an error with a value like this: &987

With these rules:

/**
     * Custom Validation Rules
     */
    validator.rule("onlyLettersNumbers", (value) => {
      console.log(value);
      return /^[a-zA-Z0-9]+$/.test(value);
    });
    validator.rule("onlyLetters", (value) => {
      console.log(value);
      return /^[a-zA-Z]+$/.test(value);
    });

Without the ampersand in the value, it works like a charm.

Specific error message in console:
TypeError: undefined is not an object (evaluating 's.replace')

Minimum and maximum rules validating numbers as strings

The minimum and maximum validation rules incorrectly validate numerical values based on their length instead of their value when programmatically passing a element's value to the validation function.

Iodine.is(element.value, ['minimum:10'])

The default validation messages for these rules are also inappropriate or confusing when validating numbers

      maximum: `Value must not be greater than '[PARAM]' in size or character length`,
      minimum: `Value must not be less than '[PARAM]' in size or character length`,

I propose deprecating the current function to prevent breaking changes, and then creating 'min' and 'max' rules for numbers and 'minlength' and 'maxlength' rules for validating string length. This would be more in keeping with the naming conventions used on html inputs.

Iodine.is is a trap for coders

Hey! I love your library, it's actually one of the best solutions I have found to validate forms on my web projects, I have been using this library with NuxtJS and I would love if in the future this library adds support for node modules.

Right now everything is being exposed to the window Object instead and there's an extra step required to expose this library to frameworks like Vue or React which is not that straightforward: you have to access the window Object after this plugin is constructed in memory by the browser, a better implementation would be to export the class as a node module and let people import it when they need it.

For people who are working on the browser though, they need instead the Window constructor of the library, one solution might be to use node scripts to compile two versions of the library: one for NPM with javascript modules and another one, minified for the browser, then everyone will be happy.

Now for my argument with this library:

The function

function iodine.is(value, rules){}

is a trap, it has a code smell that I found today when working with it, the function returns mixed values: either a boolean or a string with the area where the issue happened, and most of the time in UI code what you need is not the element that failed, but a simple yes/no true/false boolean which tells you if the field is valid or not.

So, when using this library I was confused as I spent minutes trying to solve a bug with this library because I was using this simple statement to display an error message, and I fell into the trap.

return Iodine.is(value, ["required"])? Iodine.getErrorMessage(value, ["required"]) : "";

The function Is will never return a false and therefore you have to change this statement to this

return Iodine.is(value, ["required"]) !=== true ? Iodine.getErrorMessage(value, ["required"]) : "";

It is a tiny detail but left me with a sour taste in the mouth in regards to this library usage and the coder experience.

So I decided to make things myself and did a pull request with a new function named isValid which returns true or false.

And suddenly after the change, the library is not a trap anymore and coders can live their lives happily by coding more and debugging less.

return Iodine.isValid(value, ["required"])? Iodine.getErrorMessage(value, ["required"]) : "";

Iodine.addRule is not a function Not working !

CleanShot 2022-07-19 at 15 46 49@2x

<form
    action=""
    x-data="form()"
    @focusout="change" @input="change" @submit="submit"
>
    <h1>Register</h1>

    <label for="username">Username</label>
    <input name="username" id="username" type="text"
           x-bind:class="{'invalid':username.errorMessage}"
           data-rules='["required"]' data-server-errors='[]'>
    <p class="error-message" x-show.transition.in="username.errorMessage"
       x-text="username.errorMessage"></p>

    <label for="email">Email</label>
    <input name="email" type="email" id="email"
           x-bind:class="{'invalid':email.errorMessage}"
           data-rules='["required","email"]' data-server-errors='[]'>
    <p class="error-message" x-show.transition.in="email.errorMessage"
       x-text="email.errorMessage"></p>

    <label for="password">Password</label>
    <input name="password" type="password" id="password"
           x-bind:class="{'invalid':password.errorMessage}"
           data-rules='["required","minimum:8"]' data-server-errors='[]'>
    <p class="error-message" x-show.transition.in="password.errorMessage"
       x-text="password.errorMessage"></p>

    <label for="passwordConf">Confirm Password</label>
    <input name="passwordConf" type="password" id="passwordConf"
           x-bind:class="{'invalid':passwordConf.errorMessage}"
           data-rules='["required","minimum:8","matchingPassword"]' data-server-errors='[]'>
    <p class="error-message" x-show.transition.in="passwordConf.errorMessage"
       x-text="passwordConf.errorMessage"></p>

    <input type="submit">
</form>

<script>
    function form() {
        console.log('fdf');
        console.log(Iodine);
        return {
            inputElements: [],
            init: function () {
                //Set up custom Iodine rules
                Iodine.addRule(
                    "matchingPassword",
                    value => value === document.getElementById("password").value);

                Iodine.messages.matchingPassword = "Password confirmation needs to match password";
                //Store an array of all the input elements with 'data-rules' attributes
                this.inputElements = [...this.$el.querySelectorAll("input[data-rules]")];
                this.initDomData();
                this.updateErrorMessages();
            },
            initDomData: function () {
                //Create an object attached to the component state for each input element to store its state
                this.inputElements.map(ele => {
                    this[ele.name] = {
                        serverErrors: JSON.parse(ele.dataset.serverErrors),
                        blurred: false
                    };
                });
            },
            updateErrorMessages: function () {
                //map throught the input elements and set the 'errorMessage'
                this.inputElements.map(ele => {
                    this[ele.name].errorMessage = this.getErrorMessage(ele);
                });
            },
            getErrorMessage: function (ele) {
                //Return any server errors if they're present
                if (this[ele.name].serverErrors.length > 0) {
                    return input.serverErrors[0];
                }
                //Check using iodine and return the error message only if the element has not been blurred
                const error = Iodine.is(ele.value, JSON.parse(ele.dataset.rules));
                if (error !== true && this[ele.name].blurred) {
                    return Iodine.getErrorMessage(error);
                }
                //return empty string if there are no errors
                return "";
            },
            submit: function (event) {
                const invalidElements = this.inputElements.filter(input => {
                    return Iodine.is(input.value, JSON.parse(input.dataset.rules)) !== true;
                });
                if (invalidElements.length > 0) {
                    event.preventDefault();
                    document.getElementById(invalidElements[0].id).scrollIntoView();
                    //We set all the inputs as blurred if the form has been submitted
                    this.inputElements.map(input => {
                        this[input.name].blurred = true;
                    });
                    //And update the error messages.
                    this.updateErrorMessages();
                }
            },
            change: function (event) {
                //Ignore all events that aren't coming from the inputs we're watching
                if (!this[event.target.name]) {
                    return false;
                }
                if (event.type === "input") {
                    this[event.target.name].serverErrors = [];
                }
                if (event.type === "focusout") {
                    this[event.target.name].blurred = true;
                }
                //Whether blurred or on input, we update the error messages
                this.updateErrorMessages();
            }
        };
    };
</script>

API change

Instead of exporting a Class, we could export an object containing all these helper functions

This helps us destructure functions we want resulting in treee shaking and better compression

this also helps us to write

import { isArray, isSame } from '@kingshott/iodine'

Showing valid email for invalid email address

I was validating random keywords in the below format and the validation does not show any error.

Email: lsdflksdajfkl;@kdsjf;ksadjf;s.dsfk

We can try the below regex for validating emails and it seems to be more accurate:

^[a-zA-Z0-9.!#$%&'+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)$

this in addRule?

HTML:
data-rules='["required", "ExampleRuleName"]'
JS:
Iodine.addRule("ExampleRuleName",(value, param) => { console.log("Current Input: ", this); ...

1.) Is there something like this in addRule?
2.) I have rules that are linked or dependent on other elements. Can I therefore specify a "target element" via the HTML, which is used for verification?

Maybe in that way?
HTML:
data-rules='["required", "ExampleRuleName({ target: '#idToTargetElement', group: '[data-validate-group]' }) "]'

Iodine.is(field, ['required', 'integer']))

The input from a form is for most part only text, which means that if I write some numbers, they are converted into a string value. So my question is, do you have any idea why a string with value of '123' won't pass this validation?

Iodine.is(field, ['required', 'integer']))

Error after adding CDN

Hi there

Thanks for a great plugin. :) Been playing around with it for a couple of days, and it is just great.

One minor problem though. As soon as i add the CDN to mig html "<script src="https://cdn.jsdelivr.net/gh/mattkingshott/iodine/dist/iodine.min.js" defer></script>"

I get the following error in my console (see screenshot). This also happens if I remove all code related to Iodine.

image

Update a single error only

πŸ‘‹ would it make sense to have a similar function to

Iodine.setErrorMessages({ same: `Field must be '[PARAM]'` }); 

where instead of replacing the whole set, it just replaces one?

probably something simple as

 /**
   * Replace or add a single error message.
   *
   **/
function setErrorMessage(key, message) {
  this.messages[key] = message
}

 setErrorMessage("email",  "Are you sure this is a valid email?")

Usecase: I just want to update one of them https://github.com/mattkingshott/iodine/blob/master/src/iodine.js#L52 but if I replace it, I have to add the rest of them.

I think the only way to achieve this now which is still legit way is Iodine.messages["email"] = "Are you sure this is a valid email?" but maybe you shouldn't be able to do it like in general πŸ˜„

Let me know if you think this is useful and I can fire up a PR. Codewise happy to use immutability/destruction if you prefer or browser support permits

Cheers

Form validation ideas for checkbox/radio-groups ?

Not a bug, but a question related to validate checkboxes and radiobuttons :)
I want to validate a form with the rules in alpinejs and inputs that are validated with onInput and onSubmit.
I got that working for flat inputs/textareas ( as you described here: https://dev.to/mattkingshott/validating-a-form-with-iodinejs-1jon ), but I need some ideas to validate checkboxes like so: min-checked:1, max-checked:1, required

How would you validate 'required', 'minchecked', 'maxchecked' for multiple checkboxes (name="check[]") and multiple radiobuttons (name="radioitem") ?

Add option to include field name in error messages

I have a fieldset for an address field where all the components of the address are grouped so close together there isn't room for the validation message to appear right next to the corresponding fields. Instead I have to display the error messages below the field group.

Iodine doesn't support including the field name in the error message, so I just get a stack of error messages and it's unclear what they correspond to.

image

How can I trigger a rule?

I have an input for a numeric input.
Next to it is a select with EUR or %.

With EUR you can enter a number as high as you want and with % the value must not exceed 100.

How can I query the rule "selbstbehaltVereinbart" when I change the select?

Here is the code (with alpine.js):

HTML:

    <div class="flex items-end space-x-2">
      <div>
        <label for="operationalliabilitydeductible">
          Selbstbehalt vereinbart
        </label>
        <input
          class="form-input js--numCurrency"
          type="text"
          id="operationalliabilitydeductible"
          name="Betriebshaftpflicht[deductible]"
          value=""
          :class="{'invalid':operationalliabilitydeductible.errorMessage}"
          :disabled="!checked"
          data-rules='["required", "selbstbehaltVereinbart"]'
          data-server-errors='[]'
        >
      </div>
      <div>
        <select 
          class="form-select" 
          name="Betriebshaftpflicht[unit]" 
          id="SelbstbehaltEinheit"
          @change="how-to-trigger-Rule-selbstbehaltVereinbart"
        >
          <option value="euro" selected>EUR</option>
          <option value="prozent">%</option>
        </select>
      </div>
    </div>

JS:

    // Selbstbehalt vereinbart
    Iodine.addRule(
      "selbstbehaltVereinbart",
      (value, param) => {
        let valueUnit = document.querySelector("#SelbstbehaltEinheit").value;
        if(valueUnit == 'prozent' && value.replace('.','') > 100){
          return true;
        }else{
          return false;
        }
      }
    );

Unable to get custom error for "same" rule

Let's say we have 2 fields for password

<input type="text" id="password1" placeholder="Password">
<input type="text" id="password2" placeholder="Password Confirmation">

And the following setup for Iodine:

  const data = {
    password1: document.getElementById("password1").value,
    password2: document.getElementById("password2").value
  }

  const rules = {
    password1: ['required', 'string'],
    password2: ['required', 'string', { rule: 'same', param: data.password1 }],
  }
  
  const errors = {
    password1: {
      required: 'password1 is required'
    },
    password2: {
      required: 'password2 is required',
      same: 'password1 and password 2 don\'t match'
    }
  }
  
  const results = Iodine.assert(data, rules, errors);

  console.log(results);

If we enter hello for both fields, the validation is valid:

{
  "valid": true,
  "fields": {
    "password1": {
      "valid": true,
      "rule": "",
      "error": ""
    },
    "password2": {
      "valid": true,
      "rule": "",
      "error": ""
    }
  }
}

However, if we enter hello for the first password and world for the second one, we can't get the custom error message for the same rule because the value of the first password is appended to the rule's name.

{
  "valid": false,
  "fields": {
    "password1": {
      "valid": true,
      "rule": "",
      "error": ""
    },
    "password2": {
      "valid": false,
      "rule": "same:hello",
      "error": undefined
    }
  }
}

Here is a working code: https://codepen.io/tranduyhung/pen/oNJRYaX

Async validate rules

Hey Matt! First of all, thanks for the great simple plugin!

I use this library in a few projects now, but now we walk into a thing where email had to be validated against the server for being unique.
We quickfixed this for now by overwriting the is() function, but maybe you know a better solution?

Our solution right now:

Iodine.is = async function(value, rules = []) {
	if (!rules.length) return true;
  
	if (rules[0] === "optional" && this.isOptional(value)) return true;
  
	for (let index in rules) {
	  if (rules[index] === "optional") continue;
  
	  const chunks = rules[index].split(":");
	  const ruleName = chunks.shift();
	  let rule = ruleName[0].toUpperCase() + ruleName.slice(1);
  
	  let result = await this[`is${rule}`].apply(this, [value, chunks.join(":")]);
  
	  if (!result) return await rules[index];
	}
  
	return true;
};

Or is this good enough to be added?

Tim

Dunamic Input name with index vallidation

Thanks for awesome solution for validation.

I have a dynamic input call expertise. and its cloned multiple time and the input name something like this:

<input name="user[0][expertise]"/> <input name="user[1][expertise]"/> <input name="user[2][expertise]"/>

How to validate above mentioned inputs using Iodine?

Thanks in advance

Link to version 7 in readme is broken

Hi Matt,

I just spent a good hour trying to work out why my code wasn't working properly before working out that it was a breaking change at the iodine side from the latest version. That'll teach me not to explicitly include version numbers in my CDN links...

In any case, the link to version 7 in your readme delivers a 404 so would be worth updating that:

image

Optionally return an array of all validation rules that were unmet

First off, really enjoying Iodine and using it as a drop-in library for validation. The one small hiccup that I have with it is when using multiple rules to validate a value.

For instance, if I use

Iodine.is('test', ['required', 'email', 'min:5']);

I'm only getting back email since it was the first rule that was validated. If a user then fixes that issue, but didn't realize that there's also a minimum character amount, it'll error again when re-validating on min:5.

Would you accept a PR that included an optional boolean parameter for the .is() method that will check all rules and return an array of those that didn't match?

Iodine.js('test', ['required', 'email', 'min:5'], true);

// returns ['email', 'min:5'];

Nullish coalescing operator

Hi, we have a problem on old iphones (ios 12/13 at least), can we somehow replace nullish coalescing operator on 101 line?
Thanks!
image

Iodine with Sage HTML Forms (HTML Forms Plugin) and Sage 10

This is more of a question than an issue.

I am wondering if it is possible to use Iodine with Sage HTML Forms:
https://github.com/Log1x/sage-html-forms

Sage HTML Forms is a simple package for the HTML Forms plugin that allows you to easily render forms using a corresponding Blade view (if one is present) with Sage 10.

I've introduced Iodine into my WP Sage 10 theme and I've setup the following form in a Blade view.

<x-html-forms
    :form="$form"
    id="contact"
    class="flex flex-wrap w-full space-y-8"
>
    <div class="space-y-8 lg:space-y-0 lg:flex lg:space-x-8 w-full">
        <div class="w-full lg:w-1/2">
            <input
                name="name"
                id="name"
                type="text"
                class="w-full border border-gray-400 px-4 py-2 placeholder-gray-500"
                placeholder="Name..."
            >
            <span class="text-red-700 font-bold uppercase text-xs block mt-2 inline hidden"></span>
        </div>
        <div class="w-full lg:w-1/2">
            <input
                name="email"
                id="email"
                type="email"
                class="w-full border border-gray-400 px-4 py-2 placeholder-gray-500"
                placeholder="Email Address..."
            >
            <span class="text-red-700 font-bold uppercase text-xs block mt-2 inline hidden"></span>
        </div>
    </div>
    <div class="flex space-x-8 w-full">
        <div class="w-full">
            <textarea
                name="message"
                id="message"
                class="w-full border border-gray-400 px-4 py-2 placeholder-gray-500"
                placeholder="Enter your message..."
                rows="6"
            ></textarea>
            <span class="text-red-700 font-bold uppercase text-xs block mt-2 inline hidden"></span>
        </div>
    </div>
    <div class="flex w-full justify-end">
        <input
        type="submit"
        value="Submit"
        class="btn-gray-700"
        onclick="register(event)"
      />
    </div>
</x-html-forms>

And this simple script:

<script>
    // Flag for whether the form is valid
let valid = false;

// Define the validation rules
const rules = {
    name : ['required', 'string', 'minimum:3', 'maximum:30'],
    email : ['required', 'string', 'minimum:6', 'maximum:255', 'email'],
    message : ['required', 'string', 'minimum:6', 'maximum:255'],
};



/**
 * Validate the registration form and submit it.
 *
 **/
function register(event)
{
    // Prevent the standard submission of the form
    event.preventDefault();

    // Reset the flag
    valid = true;

    // Iterate through the form fields
    for (const field in rules) {

        // Locate the field's input and validation span
        let input   = document.getElementById(field);
        let message = input.nextElementSibling;

        // Hide all existing messages
        message.classList.add('hidden');

        // Validate the field's value
        let result = Iodine.is(input.value, rules[field]);

        // Handle any failed validation
        if (result !== true) {

            // Toggle the flag
            valid = false;

            // Update the form
            message.classList.remove('hidden');
            message.innerHTML = Iodine.getErrorMessage(result);

        }
    }

    // Check whether to submit the form
    if (valid) {
        document.getElementById('contact').submit();
    }
}
</script>

The validation does work on these fields but processing the form does not work.

When HTML Forms processes "Form Actions" – it simply fetches each input name to create the usable variables.

This would be an excellent tool to add to my belt and I'm sure a lot of folks in the Roots community would love it as well. Any idea what I could do differently to allow this functionality to work with the mentioned tools?

isInteger to support strings that are integers

It would be nice if the integer rule allowed for strings that contained an integer. Or for backwards compatibility and more control if there was a looser variation.

let item_1 = 7;
let item_2 = 'string';
let item_3 = ' 8 ';

Iodine.is(item_1, ['required', 'integer']); // true
Iodine.is(item_2, ['required', 'integer']); // string - 'integer'
Iodine.is(item_3, ['required', 'integer']); // true

Regex toLoweCase

Hello,
I like your library, just what you need for small projectsπŸ‘ , but you need to fix this part, because some of regex tests not working with uppercase chars )
For example this will Iodine.isRegexMatch("P54655465",/^P\d{3,}$/) always return false.

  isRegexMatch(value, expression) {
    return new RegExp(expression).test(String(value).toLowerCase());
  }

To something like

  isRegexMatch(value, expression) {
    return new RegExp(expression).test(String(value));
  }

Iodine is undefined

Hello, I am including the Iodine library from cdn to my project, but when i try to use it on my script file i get

Alpine Error: "ReferenceError: Can't find variable: Iodine"

I did a console.log to window object and i can see Iodine there.

How to manage entire attributes

Hello, I'm new to Iodine. :)

How would one add or remove an entire ARIA attribute with Iodine.js based on validity of a field?

Valid:

<label for="firstName">
  <input id="firstName" required>
</label>

Invalid:

<label for="firstName">
  <input id="firstName" required aria-invalid="true">
  <!-- ... -->
</label>

Is there a clever way to manage entire attributes instead of just toggling its true/false value?

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.