Mevrael / bunny

BunnyJS - Lightweight native (vanilla) JavaScript (JS) and ECMAScript 6 (ES6) browser library, package of small stand-alone components without dependencies: FormData, upload, image preview, HTML5 validation, Autocomplete, Dropdown, Calendar, Datepicker, Ajax, Datatable, Pagination, URL, Template engine, Element positioning, smooth scrolling, routing, inversion of control and more. Simple syntax and architecture. Next generation jQuery and front-end framework. Documentation and examples available.
https://bunnyjs.com
MIT License
486 stars 39 forks source link

change class of submit button in Validation ? #24

Closed kiuKisas closed 6 years ago

kiuKisas commented 6 years ago

Hi, first of all, thank's for your works, this lib save time ! I wanted to know if it's possible to add/change the submit button class when it's not validated yet (with instantly validated) in order to apply some css/js (can't submit if it's not validated for example) ? Thank's for your answer

Mevrael commented 6 years ago

Hi @kiuKisas

Thank you for feedback.

If I understood correctly, you want to disable form submission until form is valid.

Bunny Validation component is disabling button when form is submitted during validation and if there were any validation errors, user will see an error message next to each input according to your styles and make submit button enabled again, while form won't be submitted.

While you can manually call validateSection() method and change class of submit button, there is no ready solution for keeping button disabled until form becomes valid.

If you want to keep button disabled until form becomes valid, you have to:

P.S. you always should use disabled attribute of a button to prevent it from clicking and not use any CSS classes for that, they won't prevent user to interact with this element. You may change styles of disabled buttons with CSS rule like button[disabled].

kiuKisas commented 6 years ago

Thank's for your quick and complete answer.

kiuKisas commented 6 years ago

Just a tiny issue with this solution, since it's check for every inputs, it also show to user the wrong inputs that it doesn't have time to field (example with 2 inputs email and password, the user gonna see 'wrong password' just after putting is email). Anyway to perform validateSection() on the form without updating all the input ? Only on the focus one maybe ? I listen to input for having a real time check

Mevrael commented 6 years ago

Yes, @kiuKisas you will need to use checkInput() instead and because of that, you also will need to create an array/object to store a validation state for each input. You may create a function which will go through this array and if there is at least one false, set btn.disabled = true, or false, otherwise.

First of all, since you will be doing everything manually, you, probably, want also to add novalidate attribute to your form.

To get inputs, you can use Validation.ui.getInputsInSection(document.forms.myFormId).

If you want to use input event, you, probably, want to fire it only once after user finished typing, for example, with 500ms delay. You can use Bunny DOM util addEventOnce(input, eventName, handler, delay = 500) for that.

Finally, you call Validation.checkInput(input).then(() => { // valid }.catch(() => { //invalid } within event handler and update array and call function to update button state as well.

Something like:

      const inputs = Validation.ui.getInputsInSection(document.forms.login_form);
      const btn = document.getElementById('login_form_submit');
      let validInputs = {};
      for (let k = 0; k < inputs.length; k++) {
        validInputs[inputs[k].name] = false;
      }

      const checkFormAndUpdateBtn = () => {
        let disabled = false;
        for (let k = 0; k < inputs.length; k++) {
          if (validInputs[inputs[k].name] === false) {
            disabled = true;
            break;
          }
        }

        btn.disabled = disabled;
      };

      for (let k = 0; k < inputs.length; k++) {
        const input = inputs[k];
        addEventOnce(input, 'input', () => {
          Validation.checkInput(input).then(() => {
            validInputs[input.name] = true;
            checkFormAndUpdateBtn(input);
          }).catch(() => {
            validInputs[input.name] = false;
            checkFormAndUpdateBtn(input);
          })
        });
      }
kiuKisas commented 6 years ago

Ho thank's a lot ! Yeah, I saw checkInput() in the source code as a potentiel candidate for my code. I made this based on what you said: EDIT: I fixed a tiny mistake I made with input.name

let validInputs = {}
let inputs = []
let btn = undefined

function updateBtn() {
  let disabled = false
  inputs.every((input) => {
    if (validInputs[input.name] === false) {
      disabled = true
      return false
    }
    return true
  })
  btn.disabled = disabled
}

function setInputsValidation(inputsSrc, submit) {
  validInputs = {}
  inputs = []
  btn = submit

  inputsSrc.forEach((input) => {
    validInputs[input.name] = false
    inputs.push(input)
    addEventOnce(input, 'input', () => {
      Validation.checkInput(input)
      .then(() => {
        validInputs[input.name] = true
        updateBtn()
      })
      .catch(() => {
        validInputs[input.name] = false
        updateBtn()
      })
    }, 500)
  })
}

EDIT2: they have an issue with my solution when I changed the btn value faster than 500ms, so I deleted the delay value. EDIT3: I post too fast, this issue is due to an another part of code it seems.