I am doing an exercise of an online course. In this exercise I have a form with 3 inputs and I have to extract them to make a request to a server. My problem is that my JavaScript Code only returns the empty string if I log it in the console, not the changed value. I guess it's accessing the inital value of the html. How can I solve this?
JavaScript Code:
// Initial call if the form is submitted
document.querySelector("#compose-submit").onsubmit = send_mail();
// The send_mail function:
function send_mail() {
let recipients = document.querySelector('#compose-recipients').value; // Those return the empty string,
let subject = document.querySelector("#compose-subject").value; // although something was written
let body = document.querySelector("#compose-body").value; // inside
fetch("/emails", {
method: "POST",
body: JSON.stringify({
recipients: recipients,
subject: subject,
body: body
})
})
.then(response => response.json())
.then(result => {
console.log(result);
});
return false;
Corresponding html:
<h3>New Email</h3>
<form id="compose-form">
<div class="form-group">
From: <input disabled class="form-control" value="{{ request.user.email }}">
</div>
<div class="form-group">
To: <input id="compose-recipients" class="form-control">
</div>
<div class="form-group">
<input class="form-control" id="compose-subject" placeholder="Subject">
</div>
<textarea class="form-control" id="compose-body" placeholder="Body"></textarea>
<input type="submit" class="btn btn-primary" id="compose-submit"/>
</form>
In the first line when you are assigning a callback to the onsubmit event, you need to just pass the function name and not call it.
So, changing your first line of code to
document.querySelector("#compose-submit").onclick = send_mail;
or
bind your event to the form element to make it work with onsumbit event
document.querySelector("#compose-form").onsubmit = send_mail;
should work.
Here's a JSFiddle as a sample (check console)
Change your input type to button to prevent reloading the page after submitting. And you will keep your values
// Initial call if the form is submitted
document.querySelector("#compose-submit").addEventListener('click', () => {
send_mail();
});
// The send_mail function:
function send_mail() {
let recipients = document.querySelector('#compose-recipients').value; // Those return the empty string,
let subject = document.querySelector("#compose-subject").value; // although something was written
let body = document.querySelector("#compose-body").value; // inside
console.log(recipients);
console.log(subject);
console.log(body);
fetch("/emails", {
method: "POST",
body: JSON.stringify({
recipients: recipients,
subject: subject,
body: body
})
})
.then(response => response.json())
.then(result => {
console.log(result);
});
return false;
}
<h3>New Email</h3>
<form id="compose-form">
<div class="form-group">
From: <input disabled class="form-control" value="{{ request.user.email }}">
</div>
<div class="form-group">
To: <input id="compose-recipients" class="form-control">
</div>
<div class="form-group">
<input class="form-control" id="compose-subject" placeholder="Subject">
</div>
<textarea class="form-control" id="compose-body" placeholder="Body"></textarea>
<input type="button" value="Submit" class="btn btn-primary" id="compose-submit" />
</form>
It's because of the submit type, when you submit the form, it submits the values of the form to the given url path of your action attribute of the form <form action="path_to_fetch" method="POST"> and then refreshed the page after. So your javascript code can't catch the values of the form.
One solution is to prevent the form to be refreshed and let your javascript code do the fetching method.
so in your js code, do this:
// Initial call if the form is submitted
document.querySelector("#compose-submit").addEventListener("click", send_mail);
// The send_mail function:
function send_mail(e) {
let recipients = document.querySelector('#compose-recipients').value; // Those return the empty string,
let subject = document.querySelector("#compose-subject").value; // although something was written
let body = document.querySelector("#compose-body").value; // inside
fetch("/emails", {
method: "POST",
body: JSON.stringify({
recipients: recipients,
subject: subject,
body: body
})
})
.then(response => response.json())
.then(result => {
console.log(result);
}).finally(() => {
document.querySelector('#compose-recipients').value = "";
document.querySelector("#compose-subject").value = "";
document.querySelector("#compose-body").value = "";
})
}
Use the finally function to empty the form after submitting the form.
EDIT:
And also change your button type to just button, using the submit type will cause the refresh.
<button class="btn btn-primary" id="compose-submit" type="button">Submit</button>
Related
Any help appreciated. I've got an app that pulls data from google books api. From each book page, the user is able to leave a review. The path to the review is /review/${isbn Number}. Each page has a path based on the isbn. The review routes work and I'm able to make the post request through insomnia/postman with no issues, I'm just having trouble with the front-end js in pulling the data from the input boxes to make the post request. I'm not sure if the issue is because the isbn being in the path. Below is my front-end javascript that I am unable to fix.
const newFormHandler = async (event) => {
event.preventDefault();
console.log("testing")
const description = document.querySelector('#description').value;
const reviewTitle = document.querySelector('#reviewTitle').value;
const isbn = window.location.search
if (description) {
const response = await fetch(`api/review/${isbn}`, {
method: 'POST',
body: JSON.stringify({ description, reviewTitle }),
headers: {
'Content-Type': 'application/json',
},
});
if (response.ok) {
document.location.reload();
} else {
alert('Failed to create review');
}
}
};
document
.querySelector('.form-group')
.addEventListener('submit', newFormHandler);
My form is below:
<div class="col form-group">
<div class ="card reviewCard" style = "background-color:#fcf8f3; color: #65625e;">
<form id="blog-form">
<div>
<label for="reviewTitle">Review Title</label>
<input
value="{{title}}"
id="reviewTitle"
name="reviewtitle"
placeholder="Enter Review Title"
type="text"
required="required"
class="form-control"
data-bv-notempty="true"
data-bv-notempty-message="The title cannot be empty"
/>
</div>
<div>
<label for="review">Review</label>
<textarea
id="description"
name="review"
cols="40"
rows="10"
required="required"
class="form-control"
>{{description}}</textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
</div>
</div>
And here is my route that works fine with insomnia, no issues.
router.get('/review/:id', async (req, res) => {
try {
const isbn13 = req.params['id'];
const reviewData = await Review.findAll({ where: {
isbn:isbn13
},
include: [
{
model: User,
attributes: ['name'],
}
]
})
const reviews = reviewData.map((review) => review.get({ plain:true}));
// console.log(isbn13);
res.render('review', {
isbn: isbn13, reviews:reviews
});
} catch (err) {
console.log(err)
}
});
Any help appreciated. I tried to pull in the isbn number from the path, but with no success. I think I have it formatted wrong somehow.
First console log your req
You should see the body containing some data.
In a get request the they are arguments in the URL.
In a Psot request they are in the body of the request.
I am new to working with databases. I've been trying to create a login/register webpage using only HTML, Js and MongoDB in my codes in order to practice. I have successfully made a function for login, yet I've been struggling to create a function for registering using the Fetch API.
I am aware that my register code is used rather for a login function, but I used it as a template for the sign up one.
I'd appreciate it if anyone can help me fix the register function using Fetch() in order to not give me 401 and to be able to add the new user's email and password to my database. Thank you.
const btnAccount = document.querySelector('.account .submit')
btnAccount.addEventListener('click', event => {
event.preventDefault()
const email = emailAccount.value
const pass = passAccount.value
const pass2 = pass2Account.value
if (email && pass && pass2) {
if (pass === pass2) {
// The data i wish to add to my mongoDB users database:
const account = {
strategy: "local",
email : emailAccount.value,
password: passAccount.value
}
fetch('http://localhost:3030/authentication', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(account)
}).then(response => {
return response.json()
}).then(result => {
console.log(result)
document.forms[1].reset();
})
.catch(err => {
// If I got some errors regardings the DB or in the code itself:
console.log('eroare:', err)
alert(`Something's wrong. I can feel it!`)
})
}
else {
// Passwords not the same:
alert('Parolele nu coincid!')
}
}
else {
// Not all fields written:
alert('Completeaza bah campurile...')
}
})
<main>
<form class="account">
<div>
<label for="email">Email:</label>
<input required type="email">
</div>
<div>
<label for="password">Password:</label>
<input required type="password" class="password">
</div>
<div>
<label for="password2">Verify Password:</label>
<input type="password" class="password2">
</div>
<div>
<button class="submit">Create new account</button>
</div>
<div>
I already have an account
</div>
</form>
<button class="fetchItems">Load ITEMS</button>
<div class="output"></div>
</main>
Javascript Vue: Where does the variable e in onFileChange(e) originate?
In the following code, there is a variable e in onFileChange(e), where does it originate? It is never declared or imported in the code, so how can it be valid?
Any help would be greatly appreciated.
<template>
<div class="container" style="margin-top: 50px;">
<div class="text-center">
<h4>File Upload with VueJS and Laravel</h4>
<br />
<div style="max-width: 500px; margin: 0 auto;">
<div v-if="success !== ''" class="alert alert-success" role="alert">
{{success}}
</div>
<form #submit="submitForm" enctype="multipart/form-data">
<div class="input-group">
<div class="custom-file">
<input
type="file"
name="filename"
class="custom-file-input"
id="inputFileUpload"
v-on:change="onFileChange"
/>
<label class="custom-file-label" for="inputFileUpload"
>Choose file</label
>
</div>
<div class="input-group-append">
<input type="submit" class="btn btn-primary" value="Upload" />
</div>
</div>
<br />
<p class="text-danger font-weight-bold">{{filename}}</p>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log("Component successfully mounted.");
},
data() {
return {
filename: "",
file: "",
success: ""
};
},
methods: {
onFileChange(e) {
//console.log(e.target.files[0]);
this.filename = "Selected File: " + e.target.files[0].name;
this.file = e.target.files[0];
},
submitForm(e) {
e.preventDefault();
let currentObj = this;
const config = {
headers: {
"content-type": "multipart/form-data",
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]')
.content
}
};
// form data
let formData = new FormData();
formData.append("file", this.file);
// send upload request
axios
.post("/store_file", formData, config)
.then(function(response) {
currentObj.success = response.data.success;
currentObj.filename = "";
})
.catch(function(error) {
currentObj.output = error;
});
}
}
};
</script>
That declaration is triggered by your template, where you are binding change event to the method. The whole event as parameter gets passed to the method, Refer this section of Vue docs for better information https://v2.vuejs.org/v2/guide/events.html#Method-Event-Handlers
When a variable is called e it is usually the event. You can always console.log(e) and read its properties in the browser console.
But according to this example e is the file that is uploaded:
methods: {
thumbUrl (file) {
return file.myThumbUrlProperty
},
onFileChange (file) {
// Handle files like:
this.fileUploaded = file
}
}
onFileChange(e) has e as event related to the dom. Since while assigning the function in html if there is no parameter passed, the event as a parameter is automatically passed by javaScript.
The declaration onFileChange(e) {
declares a function with the name onFileChange that takes a single parameter e. That is what introduces the variable into the function body.
I am trying to retrieve data from a Bootstrap form element, and save it to a PostgresSQL database using Express and Knex. There are no errors when I run the route; however, the data from the form is saved as null. Here is my form element (I'm using React):
render() {
return (
<form>
<div className ="form-group">
<label>Add a Note:</label>
<textarea className="form-control" name="note" rows="5">
</textarea>
</div>
<button onClick={this.handleClick} className="btn btn-primary"
type="submit">Submit</button>
</form>
)
}
Here is my fetch to the POST route:
handleClick(e) {
e.preventDefault()
fetch('/create-note', {
method: 'POST'
})
}
Here is my Express POST route (app.use(bodyParser.json()) is included in this file):
app.post('/create-note', (req, res) => {
postNote(req.body.note)
.then(() => {
res.sendStatus(201)
})
})
Here is the Knex postNote function:
export function postNote(newNote) {
const query = knex
.insert({ note_content: newNote })
.into('notes')
return query
}
Any help would be appreciated!
With POST requests you may have to wait for data body to be ready. Try this
app.post('/create-note', (req, res) => {
var body = '';
request.on('data',function(data) { body += data; });
request.on('end', function(data) {
postNote(body)
.then(() => {
res.sendStatus(201)
})
});
})
try the following in your markup, and forgo using fetch
...
<form method="POST" action="/create-note" enctype='application/json'>
...
</form>
...
or since the default encoding for a form is application/x-www-form-encoded (doc), add the following middleware to your express app..
...
app.use(bodyParser.urlencoded({ extended: true }));
...
also you could try...
...
<button ref="form" onClick={this.handleClick} className="btn btn-primary"
type="submit">Submit</button>
...
along with
handleClick(e) {
e.preventDefault();
const data = new FormData(this.refs.form);
fetch('/create-note', {
method: 'POST',
body: data
})
}
I found a solution and want to post it incase anyone else runs into a similar issue. The problem was I wasn't querying textarea's value correctly, so I was passing an undefined variable to the database to save.
Here's the solution I came up with:
handleSubmit(e) {
const data = new FormData(e.target)
const text = {note: data.get('note')}
fetch('/create-note', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(text)
})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className ="form-group">
<label>Add a Note:</label>
<textarea className="form-control" name="note" rows="5">
</textarea>
<button ref="textarea" className="btn btn-primary"
type="submit">Submit</button>
</div>
</form>
)
}
I put a onSubmit event listener on the form, and created a new FormData instance with the form. Then I created an object containing the value of the textarea to pass into the fetch call.
I'm really stuck on how I would work with submitting a form that makes an ajax request using Vue.js and vue-resource then using the response to fill a div.
I do this from project to project with js/jQuery like this:
view in blade
{!! Form::open(['route' => 'formRoute', 'id' => 'searchForm', 'class' => 'form-inline']) !!}
<div class="form-group">
<input type="text" name="id" class="form-control" placeholder="id" required="required">
</div>
<button type="submit" class="btn btn-default">Search</button>
{!! Form::close() !!}
js/jquery
var $searchForm = $('#searchForm');
var $searchResult = $('#searchResult');
$searchForm.submit(function(e) {
e.preventDefault() ;
$.get(
$searchForm.attr('action'),
$searchForm.serialize(),
function(data) {
$searchResult.html(data['status']);
}
);
});
What I've done/tried so far in Vue.js:
view in blade
{!! Form::open(['route' => 'formRoute', 'id' => 'searchForm', 'class' => 'form-inline']) !!}
<div class="form-group">
<input type="text" name="id" class="form-control" placeholder="id" required="required">
</div>
<button type="submit" class="btn btn-default" v-on="click: search">Search</button>
{!! Form::close() !!}
vue/js
Vue.http.headers.common['X-CSRF-TOKEN'] = document.querySelector('#token').getAttribute('value');
new Vue({
el: '#someId',
data: {
},
methods: {
search: function(e) {
e.preventDefault();
var req = this.$http.get(
// ???, // url
// ???, // data
function (data, status, request) {
console.log(data);
}
);
}
}
});
I'm wondering if it's possible to use components when dealing with the response to output the response data to a div?
Just to summarise everything:
How do I submit a form using vue js and vue-resource instead of my usual jQuery way?
Using a response from ajax, how can I output data into a div preferably using components?
I used this approach and worked like a charm:
event.preventDefault();
let formData = new FormData(event.target);
formData.forEach((key, value) => console.log(value, key));
In order to get the value from input you have to use v-model Directive
1. Blade View
<div id="app">
<form v-on="submit: search">
<div class="form-group">
<input type="text" v-model="id" class="form-control" placeholder="id" required="required">
</div>
<input type="submit" class="btn btn-default" value="Search">
</form>
</div>
<script type="text/javascript">
// get route url with blade
var url = "{{route('formRoute')}}";
Vue.http.headers.common['X-CSRF-TOKEN'] = document.querySelector('#token').getAttribute('value');
var app = new Vue({
el: '#app',
data: {
id: '',
response: null
},
methods: {
search: function(event) {
event.preventDefault();
var payload = {id: this.id};
// send get request
this.$http.get(url, payload, function (data, status, request) {
// set data on vm
this.response = data;
}).error(function (data, status, request) {
// handle error
});
}
}
});
</script>
If you want to pass data to component the use 'props' see docs for more info
http://vuejs.org/guide/components.html#Passing_Data_with_Props
If you want use laravel and vuejs together, then checkout
https://laracasts.com/series/learning-vuejs
Add v-model="id" on your text input
then add it to your data object
new Vue({
el: '#someId',
data: {
id: ''
},
methods: {
search: function(e) {
e.preventDefault();
var req = this.$http.get(
'/api/search?id=' + this.id,
function (data, status, request) {
console.log(data);
}
);
}
}
});
It’s better to remove v-on="click: search" and add v-on="submit: search" on the form tag.
You should add method="GET" on your form.
Make sure you have #someId in your html markup.