I have a registration form in one of my reactJs files, which takes all values of input fields (works as I expect).
SignUp.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class SignUpForm extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
username: '',
hasAgreed: false,
formErrors: {email: '', password: ''},
emailValid: false,
passwordValid: false,
formValid: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
let target = e.target;
let value = target.type === 'checkbox' ? target.checked : target.value;
let name = target.name;
this.setState({[name]: value});
}
handleSubmit(e) {
e.preventDefault();
console.log('The form was submitted with the following data:');
console.log([this.state.email, cryptr.encrypt(this.state.password), this.state.username, this.state.hasAgreed]);
//I want to send my above data to node server
}
render() {
return (
<div className="FormCenter">
<form onSubmit={this.handleSubmit} className="FormFields" method="POST" action="/register">
<div className="FormField">
<label htmlFor="name">Username</label>
<input type="text" id="name" placeholder="Enter your username" name="username" value={this.state.username} onChange={this.handleChange} />
</div>
<div className="FormField">
<label htmlFor="password">Password</label>
<input type="password" id="password" placeholder="Enter your password" name="password" value={this.state.password} onChange={this.handleChange} />
<div className="FormField">
<label htmlFor="email">E-Mail Address</label>
<input type="email" id="email" placeholder="Enter your email" name="email" value={this.state.email} onChange={this.handleChange} />
</div>
</div>
<div className="FormField">
<label>
<input type="checkbox" name="hasAgreed" value={this.state.hasAgreed} onChange={this.handleChange} /> I agree all statements in terms of service
</label>
</div>
</form>
</div>
);
}
}
export default SignUpForm;
Now I want to send the mentioned data to the created node server.
server.js
const express = require("express");
const bodyParser = require("body-parser");
const path = require("path");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "src/containers/user")));
//the request in which I expect input data
app.post('/register', (req, res) => {
if (!req.body) return res.sendStatus(400);
console.log(req.body, "body");
res.send('welcome, ' + req.body.username)
});
app.listen(5000, () => {
console.log("is listening on port 5000");
});
As I expect, in form tag writing method="POST" action="/register"would do it's job, but even the console.log from /register request is not responding.
Note: The next thing I should implement is to write all the data in txt file. So fetching the data in back end is a must.
What you need to do is pass the data as an object to axios like so, which you indicated you have installed in your project.
const formData = {
email: this.state.email,
password: cryptr.encrypt(this.state.password)
username: this.state.username,
hasAgreed: this.state.hasAgreed
}
axios({
method: 'post',
url: '/register',
data: formData,
config: { headers: {'Content-Type': 'multipart/form-data' }}
})
.then(function (response) {
//handle success
})
.catch(function (response) {
//handle error
});
The default behavior of a form is to submit when a submit button is clicked, or if a user hits enter. In your case however, you have a submit handler like so;
handleSubmit(e) {
e.preventDefault();
console.log('The form was submitted with the following data:');
console.log([this.state.email, cryptr.encrypt(this.state.password), this.state.username, this.state.hasAgreed]);
//I want to send my above data to node server
}
In this handler your are writing e.preventDefault(); which as the name implies, prevents the form's default behavior from happening. As a result no request is actually being made to the server.
Now the surest way I know to make this work, would be to use some kind of ajax library. You can use fetch, or something like axios.
The other way which MAY work is to remove the e.preventDefault();. Now this way is a bit more tricky. Usually react apps are not served from the same server as the api, in which case your request's url would need to look something more like this.
http://localhost:5000/register. Alternatively, you can tell webpack dev server to proxy your requests. Even then I personally am not sure this would work simply because I have never tried it.
Its important to note, either approach you choose will require the full url in the request if the react app is not served from the same place as the api, or if there is no proxying done by wepback. In other words, you may very well need http://localhost:5000/register.
My suggestion would be to use an ajax library like I mentioned.
Hope this helps.
Related
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}`)
});
I created a form to contact me on my website, for that I use EmailJS.
However when I try to send myself a mail through the contact form I got a 400 Error The service ID is invalid.
I followed every steps of that tutorial as I haven't use EmailJS before https://blog.mailtrap.io/react-send-email/
Here is my Contact component
class Contact extends React.Component {
constructor(props) {
super(props);
this.state = { feedback: '', name: 'Name', email: 'email#example.com' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
render() {
return (
<form className="test-mailing">
<h1>Let's see if it works</h1>
<div>
<textarea
id="test-mailing"
name="test-mailing"
onChange={this.handleChange}
placeholder="Post some lorem ipsum here"
required
value={this.state.feedback}
style={{width: '100%', height: '150px'}}
/>
</div>
<input type="button" value="Submit" className="btn btn--submit" onClick={this.handleSubmit} />
</form>
)
}
handleChange(event) {
this.setState({feedback: event.target.value})
}
handleSubmit() {
const templateId = 'template_id';
this.sendFeedback(templateId, {message_html: this.state.feedback, from_name: this.state.name, reply_to: this.state.email})
}
sendFeedback (templateId, variables) {
window.emailjs.send(
'gmail', templateId,
variables
).then(res => {
console.log('Email successfully sent!')
})
// Handle errors here however you like, or use a React error boundary
.catch(err => console.error('Oh well, you failed. Here some thoughts on the error that occured:', err))
}
}
And here is what I added in my index.html
`<script type="text/javascript"
src="https://cdn.jsdelivr.net/npm/emailjs-com#2.3.2/dist/email.min.js"></script>
<script type="text/javascript">
(function(){
emailjs.init("my_user_ID_here"); // Obtain your user ID at the dashboard https://dashboard.emailjs.com/integration
})();
`
To fix this, I had to swap out 'gmail' with my service ID.
sendFeedback (templateId, variables) {
window.emailjs.send(
***serviceID here***, templateId,
variables
).then(res => {
console.log('Email successfully sent!')
})
// Handle errors here however you like, or use a React error boundary
.catch(err => console.error('Oh well, you failed. Here some thoughts on the error that occured:', err))
}
The JavaScript console in my web browser helped identify this.
That was happening to me, and it was because I didn't have the account activated.
when you log in, click on 'email services' and select, for example, gmail with your account
pd: google translate
Had the same problem.
To fix it,
I had to paste NOT the 'gmail' string itself but the service_id which
is below the icon gmail
in the EmailJS website after log in. Everyone has its own specific number. Also the template_id is important to put the id generated for your template.
When you want to publish your project it is advisable to place your special ids to the .env file to stay secure.
Please try to check whether you are using the right integration id, check the id token you are using with the one under integration id on the dashboard, this was my issue
Might as well share a quick fix that would probably save someone's time. I just had the same issue while using the code below.
const notifyOwnerOfGuest = async () => {
const userId = 'user_...';
const serviceId = 'service_...';
const templateId = 'template_...';
const accessToken = 'e2e1...';
const postfields = {
user_id: userId,
service_id: serviceId,
template_id: templateId,
accessToken,
};
const response = await fetch('https://api.emailjs.com/api/v1.0/email/send', {
method: 'POST',
body: JSON.stringify(postfields),
// should explicitly add the header content-type here
});
if (!response.ok) throw await response.text();
};
I just explicitly added a Content-type header like so
headers: {
'Content-Type': 'application/json',
},
and now it works.
I'm developing a Svelte frontend app which communicates with rails API server.
I like the rails way of abstraction (esp. with simple_form gem) and I'd like to bring some ideas to Svelte. The particular problem I met is how to associate errors from the rails server with inputs on the svelte side
First, I tuned up error responses from the server; in general, they look like this:
{ errors:
{ generic: [<messages without association with particular field>],
email: ["Already taken", <msg_2>, ...],
password: ["Too short", ...],
field_N: [...]
}
}
generic errors can be "Invalid Login Credentials", "Your account is locked", "Internal Server Error", etc.
Other errors must be related to the form fields and must be displayed next to them.
Here is my current approach signup.svelte:
<script>
let email;
let password;
function registerRequest() {
let regURL = baseURL+'/api/users/';
let data = {"user": {"email": email, "password": password}};
fetch(regURL, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(response) {
if (!response.ok) {
return response.json()
}
})
// if errors present then iterate through errors object
.then(function(error) {
// find id of the message field and insert actual messages
for (let [key, value] of Object.entries(error.errors)) {
document.getElementById(key).insertAdjacentHTML('beforeend',value);
}
});
}
</script>
<style> .errormessage { color: red; } </style>
<form class="col-sm-12 col-lg-6" on:submit|preventDefault={registerRequest}>
<!-- here I need to create errormessage container for each field. -->
<!-- first one is generic to display generic errors -->
<div class="errormessage" id="generic"></div>
<label for="UserEmail">Email address</label>
<input type="email" class="form-control" id="UserEmail" aria-describedby="emailHelp" placeholder="Enter your email here..." bind:value={email}>
<div class="errormessage" id="email"></div>
<label for="UserPassword">Password</label>
<input type="password" class="form-control" id="UserPassword" placeholder="...and your password here" bind:value={password}>
<div class="errormessage" id="password"></div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
I want to unify the code above with frontend validation. If the errors object is presented - show messages next to the fields with errors. It seems to me that using getElementById is overkill. Why should I use it if my DOM never reloading? Every time on input it will search for ids. Maybe it should be listener that listens changes of the errors object? I tried to bind the custom event with Svelte
import { createEventDispatcher } from 'svelte';, but with no luck.
Please help and share your thoughts.
Your intuition is right that using getElementById in a Svelte component feels off - generally Svelte wants you to make you code declarative, where the DOM is a function of the state, rather than imperative where you're manually changing the DOM.
For the error messages, I'd suggest having an errors variable in your component. When server errors come back, you can assign them to errors, which will cause a reactive update and re-render the component. Then the markup can know to render errors when they exist, without you having to explicitly change the DOM. Something like this:
<script>
let email;
let password;
// New `errors` variable.
let errors = {};
function registerRequest() {
// Reset the errors when the user submits a new request.
errors = {};
let regURL = baseURL+'/api/users/';
let data = {"user": {"email": email, "password": password}};
fetch(regURL, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(response) {
if (!response.ok) {
return response.json()
}
})
.then(function(error) {
// Just save the errors
errors = e.errors;
});
}
</script>
<style> .errormessage { color: red; } </style>
<form class="col-sm-12 col-lg-6" on:submit|preventDefault={registerRequest}>
<!-- first one is generic to display generic errors -->
<!-- note the `|| []` part, which makes sure that
`#each` won't throw an error when `errors.generic` doesn't exist -->
{#each (errors.generic || []) as error}
<div class="errormessage" id="generic">{error}</div>
{/each}
<label for="UserEmail">Email address</label>
<input type="email" class="form-control" id="UserEmail" aria-describedby="emailHelp" placeholder="Enter your email here..." bind:value={email}>
<!-- email errors -->
{#each (errors.email || []) as error}
<div class="errormessage" id="email">{error}</div>
{/each}
<label for="UserPassword">Password</label>
<input type="password" class="form-control" id="UserPassword" placeholder="...and your password here" bind:value={password}>
<!-- password errors -->
{#each (errors.password || []) as error}
<div class="errormessage" id="password">{error}</div>
{/each}
<button type="submit" class="btn btn-primary">Register</button>
</form>
Here's a Svelte REPL with that code working. Hope that helps!
I am attempting to set up user login and role authentication with Vue, without using Vuex, as it's a bit much for the scope of our application. After a failed initial attempt to use jQuery AJAX outside of Vue, I resigned myself to making it work with Vue's data-oriented model, which I've been struggling with (I'm a designer by trade, not really a developer). My backend developer is writing in plain PHP and using mySQL, for reference.
Taking inspiration from this tutorial, I am trying to use v-model to send the form data to the server via axios.
Template:
<form class="login-form" id="loginform" #submit.prevent="login">
<div class="form-group space">
<label class="float-label" for="username">Username</label>
<input v-model="username" type="username" id="username" class="form-control" placeholder="username">
</div>
<div class="form-group">
<label class="float-label" for="username">Password</label>
<input v-model="password" type="password" id="password" class="form-control" placeholder="password">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary float-right" id="login">Log in</button>
</div>
</form>
Script:
export default {
name: 'Login',
data () {
return {
username: '',
password: ''
}
},
methods: {
login: function () {
const loginInfo = { username, password }
console.log(loginInfo)
new Promise ((resolve, reject) => {
axios({url: 'api.com/index.php', data: loginInfo, method: 'POST' })
.then(resp => {
const token = resp.data.token
localStorage.setItem('user_token', token) // store the token in localstorage
const employeeId = resp.data.employee_id
localStorage.setItem('employee_id', employeeId) // store the id in localstorage
resolve(resp)
console.log(resp);
})
.catch(err => {
localStorage.removeItem('user_token') // if the request fails, remove any possible user token if possible
reject(err)
})
})
// myLoginRoutine(loginInfo).then(() => {
// this.$router.push('/')
// })
}
}
}
The request was going through no problem, but wasn't returning anything! I decided to check and see what I was sending him... and lo and behold, const loginInfo was not the input value, as expected, but {username: input#username.form-control, password: input#password.form-control}
I am, quite frankly, very confused. I've used v-model previously on form inputs with no issues, and have no clue why this is happening or how to fix it. Any thoughts?
For future visitors: The axios data expects an object with those keys for the backend, but you don't fill the object properly.
Change
const loginInfo = { username, password }
to
const loginInfo = { username: this.username, password: this.password }
I'm trying to store the access token into the createStore (index.js) and then redirect the user to go to another webpage once they login.
For this I need to create a mutation within mutations.js so that I can set the access token and set the refresh token. The refresh should be like a time stamp.
The test.vue is where the login code is to validate the user.
So, basically I need to create a function, set the access token, set the refrefresh token and then redirect the user to another webpage once they pressh the login button.
Many thanks in advance!
index.js:
import vuex from 'vuex';
import mutations from './mutations';
const createStore = () =>{
return new vuex.Store({
state: {
accessToken: "halo",
access_token: response.data.access_token,
refresh: response.data.refresh_token
},
getters:{
accessToken(state, getters){
return state.accessToken;
}
},
mutations
});
};
export default createStore;
mutations.js:
const mutations = {
setToken(state, token) {
state.accessToken = token;
}
}
export default mutations;
test.vue:
<template>
<form>
<div class="login">
<div>
<input name="email" type="text" v-model="email" v-validate="'required'" placeholder="Email" class="eSection" id="email">
<p v-show="wrong.email">
Email is missing or incorrect. Please try again!
</p>
<i name="emailFormat" type="text" v-validate="'required|emailFormat'" placeholder="Email" class="eSection" id="email"></i>
<p v-show="wrong.emailFormat">
Not valid email!
</p>
<input name="password" type="password" v-model="password" v-validate="'required'" placeholder="Password" class="pSection"
id="password">
<p v-show="wrong.password">
Password is missing or incorrect. Please try again!
</p>
<p v-show="wrong.all">
Login Details Incorrect!
</p>
<button type="button" class="log" #click="login">LogIn</button>
</div>
</div>
</form>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
email: "test#gmail.com",
password: "123456",
flag: false,
wrong: {
email: false,
emailFormat: false,
password: false,
all: false
},
}
},
methods: {
login: function (index) {
this.wrong.email = false;
this.wrong.password = false;
this.wrong.all = false;
this.wrong.emailFormat = false;
axios.post(`https://api.ticket.co/auth/login`, {
email: this.email,
password: this.password
})
.then(response => {
// JSON responses are automatically parsed.
console.log(response.data)
console.log(response.status)
})
.catch(e => {
console.log(e)
console.log(e.response.status)
var vueObject = this
switch (e.response.status) {
case 400:
if (!vueObject.email) {
console.log(1)
this.wrong.email = true;
} else if (!vueObject.emailFormat) {
console.log(2)
this.wrong.emailFormat = true;
};
if (!vueObject.password) {
console.log(3)
this.wrong.password = true;
}
break;
case 401:
console.log(4)
this.wrong.all = true;
break;
}
})
},
},
}
</script>
I think there are two possible solution for this problem.
First: you can use vue-router. In that case, there will be no
page refresh, just component change. That way untill you refresh
the page, every vuex state will live. (But its not the best
solution).
Second; You can write a rest call, what is give you back the actual
user token, if the user authenticated. So only this rest api call use
session authentication and / or CSRF token (check wiki if you
don't know it). One of the most elegant way if you use axios
interceptor (run before every axios call), what will get the token if
the user is authenticated. Check this github comment for futher
information. (Make sure, that session timeout is longer then token
lifetime :) )