Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Hyperform with AJAX lookup... #82

Open
ZonkedCompanion opened this issue Oct 19, 2018 · 4 comments
Open

Using Hyperform with AJAX lookup... #82

ZonkedCompanion opened this issue Oct 19, 2018 · 4 comments

Comments

@ZonkedCompanion
Copy link

This isn't strictly an issue with Hyperform as such, but rather a request for more information. The documentation seems a little sparse and I was wondering if it is possible to hook an AJAX lookup into the validation of certain fields.

For example:
A username field which sends a request to see if the username has been taken... So say for simplicity sake the AJAX returns 0 for invalid and 1 for valid. Only when the value matches both the pattern and AJAX returns 1 will the field be marked as valid.

I have been tinkering with it and I cant figure out a reliable way to manually trigger valid/invalid events after firing an ajax request on validation of certain fields. As soon as submit is pressed the field is marked as valid.

If it is possible could someone provide some guidance, or even a basic example, of how to hook into Hyperform with AJAX requests?

Much appreciated.

@Boldewyn
Copy link
Contributor

Sorry for the delay!

Yes, using AJAX for validation is a bit tricky. Theoretically, one could use synchronous requests to block form submission until all validation is done, but that would make for terrible UX.

What’s usually done is to couple AJAX-based validation with the change or input events and default to “valid”, if those haven’t finished on submit (assuming, that the appropriate validation on server-side will catch the problem anyway). This allows the user to fill a form without any waiting, spinning wheels, frustration from hanging network requests, ...

Example:

var current_request;

document.querySelector('[name="foo"]').addEventListener('change', function() {
  // start with the assumption, that the input field is valid now, so that a failing
  // AJAX request below doesn't kill the ability to submit this form:
  this.setCustomValidity('');

  // if there is no value to validate, we can rely on good ol' "required"
  // to take over if needed
  if (this.value) {

    // if a request is currently running, stop it, because it's most likely using
    // outdated data
    if (current_request) {
      // aborting is actually a bit harder: https://stackoverflow.com/a/47250621/113195
      current_request.abort();
    }

    current_request = fetch('/your/validation/endpoint', {
      method: 'POST',
      body: 'value='+encodeURI(this.value),
    })
      // assuming JSON response a la {"valid":true,"msg":"what's wrong"}
      .then(function(response) { return response.json(); })
      .then(function(response) {
        // setCustomValidity() is polyfilled by Hyperform, so you can use it confidently ;)
        if (response.valid) {
          this.setCustomValidity('');
        } else {
          this.setCustomValidity(response.msg);
        }
      });
  }
});

@ZonkedCompanion
Copy link
Author

ZonkedCompanion commented Oct 26, 2018

Hi there, thanks for getting back to me. Looks like I was going about this completely the wrong way!

I have tried the code you supplied above but console returns the following errors and the field always remains valid:
"Uncaught (in promise) TypeError: this.setCustomValidity is not a function"
"Uncaught TypeError: current_request.abort is not a function"

The alert()s (which I have added for testing purposes) seem to work when the AJAX returns true/false, but the field itself doesn't get marked as invalid when false and the response.msg is not shown.

Just to confirm Hyperform.js is linked in the page head and the following script is defined just before the closing body tag, so I don't think its anything to do with load order.


hyperform(window, {
  classes: {
    valid: 'form-value-valid',
    invalid: 'form-value-invalid',
    warning: 'alert-warning',
  }
});

var current_request;
document.querySelector('[name="text_test"]').addEventListener('change', function() {
  // start with the assumption, that the input field is valid now, so that a failing
  // AJAX request below doesn't kill the ability to submit this form:
  this.setCustomValidity('');

  // if there is no value to validate, we can rely on good ol' "required"
  // to take over if needed
  if (this.value) {

    // if a request is currently running, stop it, because it's most likely using
    // outdated data
    if (current_request) {
      // aborting is actually a bit harder: https://stackoverflow.com/a/47250621/113195
      current_request.abort();
    }

    current_request = fetch('/test_json', {
      method: 'POST',
	  headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
      },
      body: 'value='+encodeURI(this.value),
    })

      // assuming JSON response a la {"valid":true,"msg":"what's wrong"}
      .then(function(response) { return response.json(); })
      .then(function(response) {
        // setCustomValidity() is polyfilled by Hyperform, so you can use it confidently ;)
        if (response.valid) {
			alert('valid');
          this.setCustomValidity('');
        } else {
			alert('not valid');
          this.setCustomValidity(response.msg);
        }
      });
  }
});

Perhaps you can shed some light on this?

@Boldewyn
Copy link
Contributor

Yes, the current_request.abort() is not, what you really need to do to abort a fetch request. I’ve added a link to how the real code would look in the comment above that line.

The other problem is related with this inside the promise callbacks being not equal to the this outside. You can fix that by setting, somewhere close to the top:

var input = this;

and then using input.setCustomValidity inside the functions.

@ZonkedCompanion
Copy link
Author

Hi Boldewyn,

Thanks again for your response. I have made some further modifications to the code you supplied and it is now working as expected.

I have changed it so that it only makes the ajax request if the value is valid using checkValidity(). This way it is not sending wasted ajax requests if the value doesn't match the pattern or fall within the min/max length. Additionally this also prevents the ajax response from "overwriting" the min/max length, required, or pattern miss-match messages.

I have also abandoned the current_request.abort(); part of this script as I don't think it is strictly necessary and it seems to work just fine without it. My sever always responds to ajax requests in a timely manner anyway.

Finally I had problems getting the response message to reliably show when ajax responds with valid:false and found that if I call reportValidity(); at the end it makes the ajax response message appear without fail.

var current_request;
function validate_by_ajax(input) {
	var input = input;

	// if there is no value to validate, we can rely on good ol' "required"
	// to take over if needed
	if (input.value) {
		
		// start with the assumption, that the input field is valid now, so that a failing
		// AJAX request below doesn't kill the ability to submit this form:
		input.setCustomValidity('');
	
		//is it valid? - run the ajax check
		if (input.checkValidity()) {

			// if a request is currently running, stop it, because it's most likely using
			// outdated data
			//if (current_request) {
			// aborting is actually a bit harder: https://stackoverflow.com/a/47250621/113195
			//current_request.abort();
			// }

			current_request = fetch('/test_json', {
				method: 'POST',
				headers: { "Content-type": "application/x-www-form-urlencoded; charset=UTF-8" },
				body: 'value='+encodeURI(input.value),
			})

			// assuming JSON response a la {"valid":true,"msg":"what's wrong"}
			.then(function(response) { return response.json(); })
			.then(function(response) {
				// setCustomValidity() is polyfilled by Hyperform, so you can use it confidently ;)
				if (response.valid) {
					input.setCustomValidity('');
				} else {
					input.setCustomValidity(response.msg);
				}
				//force response message
				input.reportValidity();
			});
	  
		}
	}	
}
document.querySelector('[name="text_test"]').addEventListener('change', function() {
	validate_by_ajax(this);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants