Track Form Submissions with Google Analytics 4 to register conversions - javascript

I need to track the submissions of a form using Google Analytics.
On my website I already have Google Analytics (GA4) o track page views:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-XXXX');
</script>
The url of the page that contains the form is /contact but might be:
/contact?topic=inquiry&utm_source=google&utm_campaign=xyzzy&utm_id=12
And a simplified version of the form is:
<form x-data="send_message()" method="post">
<label>Email</label>
<input type="text" name="email">
<button class="action" type="submit">Send</button>
</form>
When the form is submitted the submit method runs:
export default function send_message() {
submit() {
fetch("/messages", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(body)
})
.then((response) => {
if (response.ok) {
// Show success message
} else {
// Show error message
}
})
}
};
How to track the form submissions and with UTM or Topic parameters so I can measure conversions?

In order to track the form submission as a conversion is necessary to:
Create a custom event:
In the admin panel of your GA4 property go to "Events" > "Create Events" and create an event like this:
Mark your custom event as a conversion:
In the admin panel of your GA4 property go to "Conversions" > "New Conversion event" and paste the name of your custom event created in item #1 (must match)
This will only work if you turned ON "Form interactions" and "Enhanced measurement".
To check this, in the admin panel of your GA4 property go to "Data Streams" > click on your data stream > click on the gear under "enhanced measument" options and check that "form interactions" is ON.

Related

How do I create the URL to send a GET request for a form?

I am using this form.
<form action="http://localhost:3000/">
<input type="text"
id="url"
name="url"
v-model="url"
placeholder="http://">
<input type="text" id="message" name="message" value="888">
<button #click="submit" :disabled="disabled">Go</button>
</form>
Until now, pressing a button resulted in http:localhost:300?url=...&message=... page being fetched.
Now I am trying to manually override this, so I added e.preventDefault(); to the submit() function. Now I can call Fetch API to fetch this URL manually, but how to I construct the URL from the form?
All online sources show how to do it with POST, no one seems to cover GET. Not sure why, because I need an idempotent request. Is there a standard way of doing this?
You're using Vue so typically you'd use something like this
<template>
<form #submit.prevent="submit">
<input
type="text"
v-model="url"
placeholder="http://"
/>
<input
type="text"
v-model="message"
/>
<button type="submit" :disabled="disabled">Go</button>
</form>
</template>
Notes:
The <button> is a submit button without a click handler. This lets you listen for submit events on the <form> which can be triggered by mouse click or keyboard.
The #submit.prevent handles the submit event on the <form> and prevents the default action automatically
All <input> fields have an associated v-model backing a data property
Here's an example of the <script> part
const target = "http://localhost:3000/";
export default {
data: () => ({
url: "",
message: "888",
}),
computed: {
// just an example
disabled: ({ url, message }) => url.length === 0 || message.length === 0,
},
methods: {
async submit() {
const params = new URLSearchParams({
url: this.url,
message: this.message
});
const res = await fetch(`${target}?${params}`);
},
},
};
In regular JS, you can still listen for submit events on the form, capture all the fields using FormData and turn that into a query string with URLSearchParams
document.querySelector("form").addEventListener("submit", async (e) => {
e.preventDefault();
const data = new FormData(e.target);
const params = new URLSearchParams(data);
const res = await fetch(`${e.target.action}?${params}`)
});

How prevent reload after post request?

I am trying to make a post request to the server and do something with the response. Things seem to work on the server-side. The problem is that the page reloads upon completion of the response and I cannot do anything with the response.
The common suggestions are using button or preventDefault. These suggestions do not solve the problem: as you can see below, the input type is button (not submit) and preventDefault() on the event does not work.
Does anyone have an idea about what I am missing?
<form id="modelCodeForm">
<label for="codehere"></label>
<div id="modelAreaDiv">
<textarea id="modelArea" cols="60" rows="20">
stuff
</textarea>
<br>
<input id="submitUserModel" type="button" value="run on server">
</div>
</form>
function initializeUserModel(){
let model = document.getElementById("modelArea").value;
fetch('http://localhost:8080/', {
method: 'post',
headers: {'Content-Type': 'text/plain'},
body: model
})
.then(response => response.json())
.then(data => {
console.log(data);
}).then(console.log("received!"))
}
I got to the bottom of this. It turns out that the problem was due to VS Live Server which was detecting a change in the folder and hot-loading the app. The change was due to the backend, in the same folder, saving a log. Really silly thing to miss...
If you want to trigger your function when the form is submitted then you can use the "onsubmit" event listener available on HTML forms.
So you would do onsubmit="initializeUserModel(event)". You pass it the event object so you can call event.preventDefault() in the function and stop the page from reloading.
Change your input to type="submit" (or make it a button of type="submit") or the form submission won't be triggered.
<form id="modelCodeForm" onsubmit="initializeUserModel(event)">
<label for="codehere"></label>
<div id="modelAreaDiv">
<textarea id="modelArea" cols="60" rows="20">Stuff</textarea>
<br />
<input id="submitUserModel" type="submit" value="run on server" />
</div>
</form>
function initializeUserModel(event) {
event.preventDefault();
let model = document.getElementById("modelArea").value;
fetch("http://localhost:8080/", {
method: "post",
headers: { "Content-Type": "text/plain" },
body: model,
})
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.then(console.log("received!"));
}

Axios request in Vue.js method working when launched from "mounted()" but not when launched from html <form>

When I authenticate to Laravel Passport backend with Nuxt.js like below, it works and I get a token:
mounted() {
this.axiosGetToken()
}
But, if I run the method with a button in a form, like this:
<form #submit="axiosGetToken()">
<button type="submit">Axios login</button>
</form>
Then, I get the status (canceled) for the Laravel page in the Network tab of my browser developer's tool.
The method looks like this:
axiosGetToken() {
const url = 'http://laravel.test/oauth/token'
const params = {
client_id: 2,
client_secret: 'S0gpcgfIDgbvIHCL3jIhSICAiTsTUMOR0k5mdaCi',
grant_type: 'password',
username: 'me#home.com',
password: '1qaz#WSX'
}
const headers = {
}
this.$axios
.post(url, params, headers)
.then(response => {
// eslint-disable-next-line
console.log(response)
})
.catch(response => {
// eslint-disable-next-line
console.log(response)
})
},
What's wrong with this form ?
You should add prevent modifier :
<form #submit.prevent="axiosGetToken()">
<button type="submit">Axios login</button>
</form>
by default the submit event try to reload the page and searching an action from the back-end in order to run it, so the prevent modifier will prevent the action and allows the running of the js event handler.
As mentioned above, if you use a form with a button of type="submit", when pressing that button, it will use the default client behavior and send a request to the form action URL, adding a prevent will stop that behavior.
Although it is a valid answer, I would suggest adding a #click="axiosGetToken()" on the button.
<form>
<button type="button" #click="axiosGetToken()">Axios login</button>
</form>

Laravel Ajax 419 Error on Production but not Local Development and Nothing In /storage/logs/laravel.log

So I've run into this issue where I'm having a 419 Error code when submitting my AJAX request through my project. I know that this error is due to the CSRF Token not being passed, or not valid.
Story behind this: I've created a "maintenance mode" on my project. This maintenance mode restricts access to the front end by displaying the 503 error page, but still allows my administrators access to the backend to update the site, etc. This is done using some middleware. See the code here on my github page for more information on how I accomplish this functionality.
https://github.com/JDsWebService/ModelAWiki/commit/263a59ebba42688d4a232a5838334b9ee419504c
So maybe this is an issue with the 503 error page on my production environment? I'm not too sure.
I've taken a look at this Question and Answer on SOF, but it doesnt seem to be helping me any.
Laravel 5.5 ajax call 419 (unknown status)
Here is the production site, take a look at the console for more information: http://modelawiki.com/
Here is my code pertaining to the 419 error:
Javascript
$(document).ready(function() {
// CSRF Ajax Token
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
// Add Section Post Button
$('#subscribeButton').click(function(event) {
/* Act on the event */
// Clear Feedback Boxes
$('#valid-feedback').css("display", "none");
$('#invalid-feedback').css("display", "none");
// Input Field
var email = $('#email').val();
var token = $('meta[name="csrf-token"]').attr('content');
console.log(email);
console.log(token);
// Post
$.ajax({
type: 'POST',
url: '/email/subscribe/',
dataType: 'json',
data: {
email: email,
"_token": token,
},
success: function (data) {
// Check Server Side validation
if($.isEmptyObject(data.errors)){
// Show the Feedback Div
$('#valid-feedback').css("display", "block");
// Add the Bootsrapt Is Invalid Class
$('#email').addClass('is-valid');
// Validation Failed Display Error Message
$('#valid-feedback').text(data['success']);
// Animate the Object
$('#email').animateCss('tada', function() {});
console.log(data['success']);
}else{
// Show the Feedback Div
$('#invalid-feedback').css("display", "block");
// Add the Bootsrapt Is Invalid Class
$('#email').addClass('is-invalid');
// Validation Failed Display Error Message
$('#invalid-feedback').text(data.errors[0]);
// Animate the Object
$('#email').animateCss('shake', function() {});
console.log(data.errors);
}
},
error: function (data) {
console.log(data);
}
}); // End Ajax POST function
}); // End Click Event
// On Focus of the Email Box
$('#email').focus(function(event) {
/* Act on the event */
$('#valid-feedback').css("display", "none");
$('#invalid-feedback').css("display", "none");
});
}); // End Document Ready
HTML Form
<div class="input-group input-group-newsletter">
<input type="email" class="form-control" placeholder="Enter email..." aria-label="Enter email..." aria-describedby="basic-addon" id="email">
<div class="input-group-append">
<button class="btn btn-secondary" type="button" id="subscribeButton">Notify Me!</button>
</div>
<div id="invalid-feedback" class="invalid-feedback"></div>
<div id="valid-feedback" class="valid-feedback"></div>
</div>
Header (This shows that the CSRF token is actually on the 503 error page)
<meta name="csrf-token" content="{{ csrf_token() }}">
Again, this code works on my local environment, but not on my production environment. (I also know that AJAX requests can and are being handled in other parts of my site just fine on the production environment, so I know it's not a server issue and has to do with code)
Just in case here is my controller code as well.
// Store the Email into the database
public function subscribe(Request $request) {
// Validate the request
$validator = Validator::make($request->all(), [
'email' => 'required|email|max:255|unique:email_subscribers,email',
]);
// If the validation fails
if($validator->fails()) {
return response()->json([
'errors' => $validator->errors()->all(),
]);
}
// New Section Object
$subscription = new EmailSubscription;
// Add Name into Section Object
$subscription->email = $request->email;
// Save the Section
$subscription->save();
// Return The Request
return response()->json([
'success' => 'You have successfully subscribed! Check your email!'
]);
}
And my route
// Email Routes
Route::prefix('email')->group(function() {
// Create Post Route for subscribing
Route::post('/subscribe', 'EmailSubscriptionsController#subscribe')->name('email.subscribe');
});
Just in case it can help someone, we experienced the same issue and it was due to a server space disk issue. Therefore, Laravel was unable to write files (sessions.php notably).

Login components in vuejs

I've built a login component with the below code for users to log in the backend is flask and is using flask_login.
const LoginScreen = {
template: `
<div>
<h1>Sign In</h1>
<form id="loginform" class="pure-form">
<fieldset>
<input type="text" name="email" placeholder="email" v-model="logindetails.email"/>
<input type="password" name="password" placeholder="password" v-model="logindetails.password"/>
<button class="pure-button pure-button-primary" v-on:click="login">Login</button>
</fieldset>
</form>
</div>
`,
data: function () {
return {
logindetails:{}
}
},
methods: {
login: function(){
axios.post('/login/',
this.logindetails,
{
headers: {
'Content-type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': document.querySelector('#csrftoken').getAttribute('content')
}
}
).then(response => { this.logindetails = {};
this.$router.push({path: '/'});
}
)
.catch(function (error) {
console.log(error);
});
}
}
};
It seems to work correctly (though there are times when it asks me to log in again for seemingly no reason), however the component is putting the submitted form details into the querystring of the url ( example ).
Would anyone be able to tell me what I am doing wrong or, if I am doing this totally incorrectly, point me in the direction of a codebase/guide that is doing logins correctly?
Many thanks in advance.
Take a look at Vue v-on event modifiers to modify the default element behavior.
In your case you can do:
<button #click.prevent="login">Login</button>
Or in the form tag (ensure your submit button is type "submit"):
<form #submit.prevent="login">
with regards to the component is putting the submitted form details into the querystring of the url, the reason is that clicking on the button also trigger submit on the form.
HTML form submits form values to the server.
But normally in JS frameworks like Vue or React, form values are submited through ajax, without any page refresh. So using <form> has not much value here.
2 things you could do in this case
Remove <form> element. it should still works corectly
handle the form submit event.
For e.g.
<form #submit="handleSubmit($event)">
methods: {
handleSubmit:(e) =>{
e.preventDefault();
}
}

Categories

Resources