I'm trying to set up a really simple page to render documents based on inputs. I have a form with some inputs on one page, and I want the data appear on the next page when submitted.
I'm still learning Next and I'm trying to use the pages/api to do this but I am not sure how exactly it works.
As of now when I submit the form it is redirecting me to a JSON page with the data, how can I make is so when I hit submit, it uses the JSON data to redirect to another page displaying it?
Here is the code:
index.js
export default function Home() {
return (
<>
<h1 Name Generator</h1>
<form action="/api/form" method="post">
<div>
<label for="name">Name</label>{" "}
<input
type="text"
id="name"
name="name"
placeholder="Enter name."
required
/>
</div>
<div>
<button style={{ display: "flex" }} type="submit">
Submit
</button>
</div>
</form>
</>
);
}
pages/api/form.js
export default function handler(req, res) {
// Get data submitted in request's body.
const body = req.body;
// Optional logging to see the responses
// in the command line where next.js app is running.
console.log("body: ", body);
// Guard clause checks for name,
// and returns early if they are not found
if (!body.subject || !body.teachers) {
// Sends a HTTP bad request error code
return res.status(400).json({ data: "name not found" });
}
// Found the name.
// Sends a HTTP success code
res.status(200).json({
data: {
name: `${body.name}`,
},
});
}
here is the result page I want it to render the data into and what I tried
result.js
import { data } from "../pages/api/form";
export default function Result() {
console.log(data);
return (
<>
<>
<h1>Name Generator</h1>
<hr></hr>
<div>
<h4>Name</h4>
<hr></hr>
{data.name}
</div>
</>
))
</>
);
}
When I submit I get JSON at
host/api/form
data: {
name: "name"
}
I am not sure what to try as I believe there is a simple way to do this that I am just missing
Related
I'm doing a CRUD with vue-cli and nodejs on the server side. So I have a form like this
<template>
<div id="formRoot">
<div class="form" >
<form #submit.prevent="sendToTable" action="/create" method="post">
Name
<input type="text" v-model="row.name" name="name" />
Price
<input type="number" name="price" v-model="row.price" />
Description
<textarea v-model="row.description" rows="3" name="desc"/>
<input type="submit" id="button" value="SAVE" />
</form>
</div>
<form class="" action="create" method="post">
<input type="text" name="input">
<input type="submit" value="send">
</form>
</div>
</template>
<script>
export default{
data(){
return{
row: {
name: '',
price: '',
description: ''
}
}
},
methods: {
sendToTable() {
console.log(this.row);
this.$parent.addToTable(this.row);
}
}
}
</script>
the #submit.prevent is for avoid the page refreshing and of course I have a method named sendToTable.
in node I have this:
const path = require('path');
const morgan = require('morgan');
const app = express();
//middlewares
app.use(express.urlencoded({extended: false}));
app.use(express.static(path.resolve(__dirname, '../dist')));
app.use(morgan());
app.post('/create', (req, res) => {
console.log(req.body);
});
const port = 3000;
app.listen(port, () => {
console.log('server listening on port ' + port);
});
the problem is that the server cant get the post request, I think is because the #prevent.default property.
I tried sending a post request with postman and it works, so I'm sure the problem is in the frontend.
What should i do? How are actually coded those single page web apps that can send data to the server?
You need to actually post your form data via an HTTP request. You can use a library like Axios (very popular) or fetch (check the supported browsers list).
Another thing you appear to be doing is calling a method on this component's parent. That goes against Vue's one-way data flow and isn't optimal. The better solution is to have your component emit an event with the attached data.
For example (using fetch)
<form #submit.prevent="sendToTable" method="post" action="/create">
methods: {
async sendToTable ($event) {
const form = $event.target
// post form as a regular "submit" would
let res = await fetch(form.action, {
method: form.method,
body: new URLSearchParams(new FormData(form))
})
if (res.ok) {
// emit the "row-added" event to the parent
this.$emit('row-added', { ...this.row }) // using spread syntax to break references
} else {
// you might want to do something else here in case of an error
console.error(res)
}
}
}
and in your parent component (assuming the child component is named RowAdder)
<RowAdder #row-added="addToTable"/>
I'm starting with vue.js and I was reading this question to help me loading some posts from DB with v-for.
Below each post there are Edit and Delete buttons. I can delete each post by its ID correctly. And I can open the input to edit post title correctly too.
But I cannot save input changes when I click on save button. It returns to the initial text.
And when I click to edit it opens all the inputs titles.
Is there a way to open the specific post title and keep the changes after save it?
<div id="app" class="row mb-50">
<div v-for="(item, index) in tours" v-bind:key="item.id" id="tours" class="col-md-12 mb-30">
<div class="tour-list">
<div class="tour-list-title">
<p>
<input type="text" ref="item.id" :value="item.title" :disabled="!editingTour"
:class="{view: !editingTour}" />
</p>
</div>
<div class="tour-list-description">
<p>
{{ item.description }}
</p>
</div>
<div class="tour-list-options">
<div class="row">
<div class="col-md-6">
<span>
<button #click="editingTour = !editingTour" v-if="!editingTour"
class="btn border btn-circle tour-list-edit-btn">Edit</button>
</span>
<span>
<button #click="save" v-if="editingTour"
class="btn border btn-circle tour-list-edit-btn">Save</button>
</span>
<span>
<button #click="editingTour = false" v-if="editingTour"
class="btn border btn-circle tour-list-delete-btn">Cancel</button>
</span>
<span>
<button #click="deleteTour(item.id, index)" v-if="!editingTour"
class="btn border btn-circle tour-list-delete-btn">Delete</buton>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
vue.js:
let app = new Vue({
el: '#app',
data: {
editingTour: false,
tours: null,
errored: false,
edited: false,
deleted: false,
item: {
title: null,
description: null
}
},
created: function () {
this.searchTour()
},
methods: {
searchTour: function () {
axios.post('getPosts.php', { "token": param }).then((response) => {
this.tours = response.data;
}).catch((error) => {
this.errored = error;
});
},
editTour: function (id) {
axios.post('editPosts.php', { "token": token, "tourID": id }).then((response) => {
this.edited = response.data;
}).catch((error) => {
this.errored = error;
});
},
deleteTour: function (id) {
if (confirm('Are You sure?')) {
const index = this.tours.findIndex(item => item.id === id);
if (~index) {
axios.post('deletePosts.php', { "token": token, "tourID": id }).then((response) => {
this.deleted = response;
this.tours.splice(index, 1);
}).catch((error) => {
this.errored = error;
});
}
}
},
save: function () {
this.item.title = this.$refs['item.id'].value;
this.editingTour = !this.editingTour;
console.log(this.item.title);
}
}
});
In console.log(this.item.title); is returning undefined.
I have changed ref="item.id" to ref="title" and this.item.title = this.$refs['item.id'].value; to this.item.title = this.$refs['title'].value; but it did not work.
You should use in your input v-model instead of ref it will bind your model with the value you are editing, in general in vue we avoid direct DOM manipulation when possible, like so:
<input type="text" ref="item.id" v-model="item.title" :disabled="!editingTour"
:class="{view: !editingTour}" />
Where calling your function e.g. editTour you can pass it the item (if it's in the template to save the updated version like so:
#click="editTour(item)"
You can use the v-model directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type. Although a bit magical, v-model is essentially syntax sugar for updating data on user input events, plus special care for some edge cases.
Source : https://v2.vuejs.org/v2/guide/forms.html
Example:
<input v-model="description" placeholder="my description">
The above input value will then be binded to the description element of your data object and vice-versa - if one changes, the other is updated to the same value:
data:{
description: "default value"
}
So, when you DB request is ready you can update the value of the description within the DB method:
this.description=db.result.description
and the value of the input will also update.
Likewise, if the user changes the value of the input field, the value bound to the data element will be updated also. So, when saving back to DB:
db.update({description:this.description})
(note: the db methods here are for example purposes only. Replace with the relevant DB methods for your backend service.)
This is the strangest problem i ever had, this is the view (in jade):
extends layout
block content
h1 Edit chatroom!!
form(method="POST")
fieldset.form-group
label(for="name") Name:
input.form-control(name="name", type="text", placeholder="Enter a name" value="#{room.name}")
small.text-muted Give your chatroom a meaningful name for people to refer to it.
button.btn.btn-primary(type="submit") Save chatroom
a.btn.btn-default(href="/admin/rooms") Cancel
this is the form source from the page source code returned to the browser :
<form method="POST">
<fieldset class="form-group"><label for="name">Name:</label><input name="name" type="text" placeholder="Enter a name" value="independents" class="form-control"><small class="text-muted">Give your chatroom a meaningful name for people to refer to it.</small></fieldset>
<button type="submit" class="btn btn-primary">Save chatroom</button>Cancel
</form>
when i press the "Save chatroom" button i get this error message on the web page :
Cannot GET /admin/rooms/edit/
this express router is supposed to handel the form submission
router.route('/rooms/edit/:id')
.all(function(req, res, next) {
var roomid = req.params.id;
var room = _.find(rooms, r => r.id == roomid);
if (!room) {
res.sendStatus(404);
return;
}
res.locals.room = room;
next();
}).get(function(req, res) {
res.render('edit');
}).post(function(req, res) {
res.locals.room.name = req.body.name;
//res.redirect(req.baseUrl + '/rooms'); or we can also
res.redirect('./'); // but this is not good because if we had http://localhost:3000/admin/rooms/add/ it will take us to /add
});
the whole code for this small express app(two js files) exist in this previous question i wrote.
In your route defintion you have
if (!room) {
res.sendStatus(404);
return;
}
And it looks like in your code, you're not submitting any request to a URL that has an ID, so it's returning 404 just like you told it to.
Unless you're on a URL that does have an ID. What does the URL look like on the page this is on?
I have a form field (email signup) on the site, and the email provider wants me to submit it to their REST web service and get a response. I've never used JSON or AJAX before so floundering!
The HTML:
<form>
<input type="hidden" name="gid" value="12345678">
<input type="hidden" name="user.CustomAttribute.NewsletterPopUp" value="Global">
<input type="hidden" name="user.CustomAttribute.NewsletterOptIn" value="True">" value="True">
<input type="text" name="uemail" class="email_input_field" value="please enter your email" size="30" maxlength="64" onFocus="clearText(this)">
<input type="submit" name="signup" value="signup" class="email_submit_button">
</form>
Currently, using Javascript and using window.location to visit the URL (which creates the action instead of posting it) they want it converted to a form post action with XML response. What happens now:
$(".email_submit_button").click(function(){
var uemail = $('.email_input_field').val();
window.location = "http://example.com/automated/action.jsp?action=register&errorPage=/automated/action.jsp&gid=12345678&uemail="+uemail+"&user.CustomAttribute.NewsletterPopUp=Global&user.CustomAttribute.NewsletterOptIn=True";
return false;
}
});
I see you'r using jQuery so you can use the $.post to post to the server like this:
var url = "http://example.com/automated/action.jsp"
var data ={
"gid": form.gid,
"action": register,
"uemail": form.uemail,
"errorPage": "/automated/action.jsp",
"user.CustomAttribute.NewsletterOptIn": user.CustomAttribute.NewsletterOptIn,
"user.CustomAttribute.NewsletterPopUp": user.CustomAttribute.NewsletterPopUp
};
var success_func = function(data){
//do what you want with the returned data
};
$.post(url, data, success_func);
Documentation for $.post.
Or you can use the pure longer Ajax version it's mentioned in the documentation of the $.post.
EDIT:
I forget you can't do xhttpresuext to a different domain you need to use JSONP, here's a link to another SO post explaining everything by detail
Hope this help.
$(".email_submit_button").submit(function(e) {
// stop form from submitting
e.preventDefault();
// Grab all values
var uemail = $('.email_input_field').val();
// make a POST ajax call
$.ajax({
type: "POST",
url: "YOUR URL", // set your URL here
data: {
uemail: uemail // send along this data (can add more data separated by comma)
},
beforeSend: function ( xhr ) {
// maybe tell the user that the request is being processed
$("#status").show().html("<img src='images/preloader.gif' width='32' height='32' alt='processing...'>");
}
}).done(function( response ) {
// do something with the received data/response
//$("#status").html(response);
});
});
Not sure if ".email_submit_button" is the class given to the submit button or the form.. you need to use the id or class given to the form and not the submit button.. hope this helps
Can I use a PUT method in an HTML form to send data from the form to a server?
According to the HTML standard, you can not. The only valid values for the method attribute are get and post, corresponding to the GET and POST HTTP methods. <form method="put"> is invalid HTML and will be treated like <form>, i.e. send a GET request.
Instead, many frameworks simply use a POST parameter to tunnel the HTTP method:
<form method="post" ...>
<input type="hidden" name="_method" value="put" />
...
Of course, this requires server-side unwrapping.
XHTML 1.x forms only support GET and POST. GET and POST are the only allowed values for
the "method" attribute.
Can I use "Put" method in html form to send data from HTML Form to server?
Yes you can, but keep in mind that it will not result in a PUT but a GET request. If you use an invalid value for the method attribute of the <form> tag, the browser will use the default value get.
HTML forms (up to HTML version 4 (, 5 Draft) and XHTML 1) only support GET and POST as HTTP request methods. A workaround for this is to tunnel other methods through POST by using a hidden form field which is read by the server and the request dispatched accordingly. XHTML 2.0 once planned to support GET, POST, PUT and DELETE for forms, but it's going into XHTML5 of HTML5, which does not plan to support PUT. [update to]
You can alternatively offer a form, but instead of submitting it, create and fire a XMLHttpRequest using the PUT method with JavaScript.
_method hidden field workaround
The following simple technique is used by a few web frameworks:
add a hidden _method parameter to any form that is not GET or POST:
<input type="hidden" name="_method" value="PUT">
This can be done automatically in frameworks through the HTML creation helper method.
fix the actual form method to POST (<form method="post")
processes _method on the server and do exactly as if that method had been sent instead of the actual POST
You can achieve this in:
Rails: form_tag
Laravel: #method("PATCH")
Rationale / history of why it is not possible in pure HTML: https://softwareengineering.stackexchange.com/questions/114156/why-there-are-no-put-and-delete-methods-in-html-forms
for people using laravel
<form method="post" ...>
#csrf
#method('put')
...
</form>
Unfortunately, modern browsers do not provide native support for HTTP PUT requests. To work around this limitation, ensure your HTML form’s method attribute is “post”, then add a method override parameter to your HTML form like this:
<input type="hidden" name="_METHOD" value="PUT"/>
To test your requests you can use "Postman" a google chrome extension
To set methods PUT and DELETE I perform as following:
<form
method="PUT"
action="domain/route/param?query=value"
>
<input type="hidden" name="delete_id" value="1" />
<input type="hidden" name="put_id" value="1" />
<input type="text" name="put_name" value="content_or_not" />
<div>
<button name="update_data">Save changes</button>
<button name="remove_data">Remove</button>
</div>
</form>
<hr>
<form
method="DELETE"
action="domain/route/param?query=value"
>
<input type="hidden" name="delete_id" value="1" />
<input type="text" name="delete_name" value="content_or_not" />
<button name="delete_data">Remove item</button>
</form>
Then JS acts to perform the desired methods:
<script>
var putMethod = ( event ) => {
// Prevent redirection of Form Click
event.preventDefault();
var target = event.target;
while ( target.tagName != "FORM" ) {
target = target.parentElement;
} // While the target is not te FORM tag, it looks for the parent element
// The action attribute provides the request URL
var url = target.getAttribute( "action" );
// Collect Form Data by prefix "put_" on name attribute
var bodyForm = target.querySelectorAll( "[name^=put_]");
var body = {};
bodyForm.forEach( element => {
// I used split to separate prefix from worth name attribute
var nameArray = element.getAttribute( "name" ).split( "_" );
var name = nameArray[ nameArray.length - 1 ];
if ( element.tagName != "TEXTAREA" ) {
var value = element.getAttribute( "value" );
} else {
// if element is textarea, value attribute may return null or undefined
var value = element.innerHTML;
}
// all elements with name="put_*" has value registered in body object
body[ name ] = value;
} );
var xhr = new XMLHttpRequest();
xhr.open( "PUT", url );
xhr.setRequestHeader( "Content-Type", "application/json" );
xhr.onload = () => {
if ( xhr.status === 200 ) {
// reload() uses cache, reload( true ) force no-cache. I reload the page to make "redirects normal effect" of HTML form when submit. You can manipulate DOM instead.
location.reload( true );
} else {
console.log( xhr.status, xhr.responseText );
}
}
xhr.send( body );
}
var deleteMethod = ( event ) => {
event.preventDefault();
var confirm = window.confirm( "Certeza em deletar este conteúdo?" );
if ( confirm ) {
var target = event.target;
while ( target.tagName != "FORM" ) {
target = target.parentElement;
}
var url = target.getAttribute( "action" );
var xhr = new XMLHttpRequest();
xhr.open( "DELETE", url );
xhr.setRequestHeader( "Content-Type", "application/json" );
xhr.onload = () => {
if ( xhr.status === 200 ) {
location.reload( true );
console.log( xhr.responseText );
} else {
console.log( xhr.status, xhr.responseText );
}
}
xhr.send();
}
}
</script>
With these functions defined, I add a event listener to the buttons which make the form method request:
<script>
document.querySelectorAll( "[name=update_data], [name=delete_data]" ).forEach( element => {
var button = element;
var form = element;
while ( form.tagName != "FORM" ) {
form = form.parentElement;
}
var method = form.getAttribute( "method" );
if ( method == "PUT" ) {
button.addEventListener( "click", putMethod );
}
if ( method == "DELETE" ) {
button.addEventListener( "click", deleteMethod );
}
} );
</script>
And for the remove button on the PUT form:
<script>
document.querySelectorAll( "[name=remove_data]" ).forEach( element => {
var button = element;
button.addEventListener( "click", deleteMethod );
</script>
_ - - - - - - - - - - -
This article https://blog.garstasio.com/you-dont-need-jquery/ajax/ helps me a lot!
Beyond this, you can set postMethod function and getMethod to handle POST and GET submit methods as you like instead browser default behavior. You can do whatever you want instead use location.reload(), like show message of successful changes or successful deletion.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
JSFiddle: https://jsfiddle.net/enriquerene/d6jvw52t/53/
If you are using nodejs, you can install the package method-override that lets you do this using a middleware.
Link to documentation: http://expressjs.com/en/resources/middleware/method-override.html
After installing this, all I had to do was the following:
var methodOverride = require('method-override')
app.use(methodOverride('_method'))
I wrote an npm package called 'html-form-enhancer'. By dropping it into your HTML source, it takes over submission of forms with methods aside from GET and POST, and also adds application/json serialization.
<script type=module" src="html-form-enhancer.js"></script>
<form method="PUT">
...
</form>
In simple words - No.
I have tried to fire a put request in the HTML form, but it sends the POST request to the server. To add the PUT request -
We can do it by listening to the submit action in the script, then fire the put request to a particular endpoint.
Screenshot from the http-server env. test