Netlify Forms with Vue @submit.prevent="handleSubmit"

Hi, I’m running into a similar issue today. I’m using Fetch instead of Axios. I’m using POST, a FormData object for the body, and I keep getting 404 errors when I post. My source is as follows:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Special Contact Form</title>
</head>
<body>
	<h2>Contact Form</h2>

	<form method="post" name="Contact Form 3" action="/thankyou.html" id="myForm"
	@submit.prevent="sendForm" ref="formTag" data-netlify="true">
	<p>
		<label for="name">Name:</label>
		<input type="text" name="name" id="name" v-model="name">
	</p>
	<p>
		<label for="email">Email:</label>
		<input type="email" name="email" id="email" v-model="email">
	</p>
	<p>
		<label for="comments">Comments:</label><br/>
		<textarea name="comments" id="comments" v-model="comments"></textarea>
	</p>
	<p>
		<input type="submit">
	</p>
	</form>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
	el:'#myForm',
	data:{
		name:'',
		email:'',
		comments:''
	},
	methods:{
		async sendForm() {
			let target = this.$refs.formTag.action;
			let name = this.$refs.formTag.getAttribute('name');

			let fd = new FormData();
			fd.append('name', this.name);
			fd.append('email', this.email);
			fd.append('comments', this.comments);
			fd.append('form-name', name);

			let resp = await fetch(target, {
				method:'POST',
				headers: { 
					'Content-Type': 'application/x-www-form-urlencoded' 
				},
				body:fd
			});
//			let data = await resp.json();
//			console.log(data);
		}
	}
});

</script>

</body>
</html>

Any idea why this doesn’t work?

An update - I realized I wasn’t properly encoding my form. On a whim I changed body to a hard coded value:

name=ray&form-name=Contact+Form

This seemed to work! But then I tried to make it dynamic:

encode(data) {
	// Credit - Brian Rinaldi at https://www.stackbit.com/blog/complete-guide-netlify-forms/
 	return Object.keys(data)
	.map(key => encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))
	.join("&");
},
async sendForm() {
	let target = this.$refs.formTag.action;
	let name = this.$refs.formTag.getAttribute('name');

	let fd = {
		'name':this.name,
		'email':this.email,
		'comments':this.comments,
		'form-name':name
	};
			
	let encodedForm = this.encode(fd);
	console.log(encodedForm);
	let resp = await fetch(target, {
		method:'POST',
		headers: { 
			'Content-Type': 'application/x-www-form-urlencoded' 
		},
		body:encodedForm
	});

//			let data = await resp.json();
//			console.log(data);
}

And it failed. I couldn’t understand why my hard coded string worked and the dynamic one did not. Then I noticed something. My hard coded test was using a form name of “Contact Form.” I had a previous form on my Netlify site with that name. When I changed my form name to match, it worked.

So question - why can’t I send an Ajax submission to a form with a name that is “new” to Netlify?

Ok, so I built a new form that didn’t use Ajax. I named it “My Contact Form”. I submitted it. On my Ajax form, I used that name, and it worked. So it sounds like you can’t use an Ajax form until you submit at least once with a regular form. That’s a bug, right?

Hey @cfjedimaster,
Glad to hear you sorted out your Vue form! To make sure I understand, the next issue you raised is not about Vue, but about submitting a vanilla Javascript form using fetch. Is that right? Let us know and we’ll dig in.

Correct - it isn’t a Vue issue, but an “Ajax submit on Netlify” issue. I spoke with Brian Rinaldi and he mentioned he saw this as well.

To test, post with forn name equal to Something. It will fail. Build a simple form that just does a regular post and uses the same name. Now the fancy Ajax one will work.

Hi, @cfjedimaster, this sounds a bit like the “HTML form must exist” requirement mentioned here. Quoting:

If you’re using a pure javascript form instead, this additional step is required:

  • You still need to include an HTML form that meets the criteria above, including all the inputs with the same names as the javascript form. This is required because we parse HTML to find forms during your deploy - but we don’t try to parse javascript files. This means if you are using React, Vue, etc. which create HTML elements using Javascript during the render, you’ll still need to include an actual HTML file that includes all of the form details in it, including all possible inputs that you want to use. For a working example using React that you can generalize to other frameworks from, check out this blog post .

It sounds like the requirement above causing the behavior mentioned here?

I ask because it sounds like this is the issue:

  • javascript form doesn’t work
  • HTML form is created
  • javascript starts working

If this is the issue, that is because the HTML form is required for all forms. If I’ve misunderstood the issue, please let us know.

Close, but I am using an HTML form. Here it is:

This did not work until I built:

This is the same form but with no Ajax. As soon as I submitted that one the Ajax one worked, but again, you can see the ‘regular’ form in form3.html as well, it just uses Ajax to submit it.

Hey @cfjedimaster,
Since this is not about Vue, but about submitting a form using fetch, I don’t think it’s useful to test with Vue as you’re doing in the first example. This example works for me on first submit, without sending a non-fetch form first:



<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Special Contact Form</title>
  </head>
  <body>
    <h2>Contact Form</h2>
    <form method="post" name="Contact Form" id="myForm" data-netlify="true">
      <p>
        <label for="name">Name:</label>
        <input type="text" name="name" id="name">
      </p>
      <p>
        <label for="email">Email:</label>
        <input type="email" name="email" id="email">
      </p>
      <p>
        <label for="comments">Comments:</label><br/>
        <textarea name="comments" id="comments"></textarea>
      </p>
      <p>
        <button type="submit">Submit</button>
      </p>
    </form>
  </body>
  <script>
    const handleSubmit = (e) => {
      e.preventDefault()
      let myForm = document.getElementById('myForm');
      let formData = new FormData(myForm)
      fetch('/', {
    	method: 'POST',
    	body: formData
      })
    	.then(() => alert('success!'))
    	.catch((error) => alert(error))
    }

    document.querySelector("form").addEventListener("submit", handleSubmit);
  </script>
</html>

Please let us know if you’re seeing something different.

Fascinating - it did. Now - while I don’t think my Vue code is perfect, I honestly didn’t expect an issue there. And oddly, by using a new name and it working, maybe it’s something else. Doing a test now.

Nope, it still won’t work for me. I noticed you post to /. I tried that as well and it doesn’t help. I also notice you aren’t encoding your formdata - I’ll give that a shot next.

Also - if you are copying the form data with FormData(myForm), you are not passing a value called form-name with the name of the form. I thought that was required?

EDIT: Never mind - forgot that the hidden form field is added automatically.

AHAH! That’s getting me somewhere. When I view source on form3.html vs form5.html (what I called your code), your code has the hidden form tag. Netlify is not “seeing” my form tag:

<form method="post" name="My Contact Form 7" action="/" id="myForm"  @submit.prevent="sendForm" ref="formTag" data-netlify="true">

Why would it miss it?

EDIT! AHAH! So when I remove @submit.prevent="sendForm" ref="formTag" Netlify “sees” the form and adds the hidden form tag. Of course that breaks the Vue application. So is the regex that yall use somehow ‘confused’ by the Vue-ism? It shouldn’t be - but that would be a real bug you could test, right?

EDIT: It looks to be specifically @submit.prevent="sendForm" that breaks the regex.

Hey @cfjedimaster,
We just tested https://github.com/cfjedimaster/NetlifyTestingZone/blob/master/form3.html and the submission was successfully received. Please let us know if you’re seeing something different.

That’s because I updated it with a fix. :slight_smile: Can you please test with the original code where @submit is used in the form tag? I believe this breaks the regex (I assume) you look when scanning for netlify forms.

Hey @cfjedimaster,
Sorry for the delay here, got a bit lost in all the different issues and form versions here. Could you please share a live version of the form you would like us to investigate? Thanks!

New form is here: https://github.com/cfjedimaster/NetlifyTestingZone/blob/master/form10.html
Online it is here: https://netlifydemos.netlify.app/form10.html

If you view source, you will see Netlify didn’t “recognize” the form tag needed to be processed.

Hey @cfjedimaster,
This is what @luke was getting at earlier: that because you’re no longer submitting a pure HTML form at this point (that’s what you’re doing by adding Javascript inside the form tag), you will need to also include a pure HTML replica of the form in order for our form bots to pick it up:

So the body of your html file would look like this:

	<h2>Contact Form</h2>

	<form type="hidden" name="My Contact Form 10" data-netlify="true" >
		<input type="hidden" name="name">
		<input type="hidden" name="email">
		<input type="hidden" name="comments">
	</form>

	<form method="post" name="My Contact Form 10" action="/" id="myForm" @submit.prevent="sendForm" ref="formTag">
	<p>
		<label for="name">Name:</label>
		<input type="text" name="name" id="name" v-model="name">
	</p>
	<p>
		<label for="email">Email:</label>
		<input type="email" name="email" id="email" v-model="email">
	</p>
	<p>
		<label for="comments">Comments:</label><br/>
		<textarea name="comments" id="comments" v-model="comments"></textarea>
	</p>
	<p>
		<input type="submit">
	</p>
	</form>

I think about Netlify forms as having two different components:

  1. we need a version of your form that is legible to our HTML parser, that we can use to say “we’re expecting submissions to the form with x name, in y shape”;
  2. and then, apart from that, you need code to handle inputs and send them to that form that we set up for you, in the format that we expect.

In a perfect world (i.e., with pure HTML forms), these two components live in the same form, with very little additional configuration. But when they don’t, we have to do things like add a hidden form as I did above.

Let us know if this helps or if you have other questions.

Respectfully, I don’t think you are right - or I’m doing a poor job of explaining myself. :wink: I was able to get one form working with Vue just fine. Everything started working when I removed @submit.prevent from the form tag. I noticed in View Source that with that portion gone, Netlify would fine the form, add the hidden form field, and things worked perfectly fine.

If you look at https://github.com/cfjedimaster/NetlifyTestingZone/blob/master/form3.html, you can see I have one form and use Vue to process it with Netlify getting the form submission fine.

If I had to guess, your code looks for netlify or data-netlify in <form> tags, and the @submit part breaks it.

Does that make sense? To be clear, I think your solution would work. But if it’s really just a regex issue perhaps, that could be fixed maybe, right?

Hi there,

we haven’t forgotten about this - we’ll get back to you soon as soon with more info.

Thanks for your patience! We finally took that exact question to our developers and they said “ah, yes, we’ll fail to process, if any attribute in the form definition starts with @ or other special characters”.

I don’t think we’ll be changing that behavior, so hopefully you can work around it.

I was able to - but this is really something that should be documented. I don’t know React, but I’d imagine this would impact multiple front end technologies that use templating. It would be a quick one line addition to the docs I’d be happy to write. You can copy and paste. Etc etc. :slight_smile: Please do so!