Send JSON data to server from react without rerender the page - mongodb

I'm a bit new to ReactJS,
I've created a simple form and table, each time I hit the submit the state of the table change with the new data.
I'm using tornado as a backend server and mongodb as my DB.
I'm looking for a way to send the same JSON to the server on a second pipe (without rerendering the page again.)
How can I do it?
---- edit - react component -----
import Re
act from 'react';
export default class Reblaze_Dashboard extends React.Component {
constructor(props) {
super(props);
this.onNewUser = this.onNewUser.bind(this);
this.state = {
data: window.obj,
};
}
onNewUser(e) {
e.preventDefault();
const formData = {};
for (const field in this.refs) {
formData[field] = this.refs[field].value;
}
this.state.data.push(formData);
this.setState({
data: this.state.data,
});
}
render() {
return(
<div>
<h2>Dashboard page</h2>
<form onSubmit={this.onNewUser}>
<div className="form-group">
<label htmlFor="first-name-input">First name:</label>
<input type="text" className="form-control" ref="firstname" id="first-name-input" />
</div>
<div className="form-group">
<label htmlFor="last-name-input">Last name:</label>
<input type="text" className="form-control" ref="lastname" id="last-name-input" />
</div>
<input type="submit" value="Submit" className="btn btn-primary pull-right" />
</form>
<br /><br />
<div className="table-responsive">
<table className="table table-striped table-hover">
<thead>
<tr>
<td><h4>First name</h4></td>
<td><h4>Last name</h4></td>
</tr>
</thead>
<tbody>
{this.state.data.map((line, key) =>
<tr key={key}>
<td>{line.firstname}</td>
<td>{line.lastname}</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
}
}

You're submitting form data and immediately updating table without checking if your data reaches the server. This way it is possible to update user view while failing to update the database.
I strongly recommend to use some promise-based HTTP-client like Axios (https://github.com/mzabriskie/axios). Send some request, then wait for the promise resolve, and only after that update your component state.
You need something like this:
import React from 'react';
import axios from 'axios';
/*...*/
onNewUser(e) {
e.preventDefault();
const formData = {};
for (const field in this.refs) {
formData[field] = this.refs[field].value;
}
axios.post('yourApiUrl', formData)
// if request is sent and status is ok, update form and send second request
.then((res) => {
this.state.data.push(formData);
this.setState({
data: this.state.data,
});
return axios.post('yourApiUrl', formData);
})
// if second request is ok, receive a notification
.then((res) => {
console.log('second request is ok');
})
// if there is an error, receive a notification
.catch((err) => {
console.log(err);
})
}
/*...*/

Related

How can i dynamically add an Input select element on click of a button in ember

Am creating an ember application where am in need of dynamicaly adding a select element which will have options fetched from a server. so the select elements look like this. And instead of having all dropdown boxes predefined i need to add them dynamicaly like on a click of a button like( + add more). like
and each of those drop down boxes should contain the datas that is fetched from the server. plus i need a way to get the datas from those dynamically created select fields.
my .hbs for the current drop down page is..
map.hbs
<center><h4>Map</h4></center>
<container class = "cond">
{{#each this.model.sf as |row|}}
<select class = "sel">
{{#each this.model.sf as |sf|}}
<option value = {{sf.attrname}}>{{sf.attrname}}</option>
{{/each}}
</select><br>
{{/each}}
I tried ember-dynamic-fields but its depracted and I couldnt able to use it.. and all other solutions on web or for ember way older versions.. nothing works on ember 4.6 so could anyone helpout?
Using The Platform's native FormData functionality, demo'd here.
I think we can generate any number of inputs based on input data in the following way:
Store the form's state in some variable
conditionally show further select / inputs based on the properties in that form state.
Code-wise, that'd look like this:
{{#if (dataHasValueFor "fieldName")}}
Show previously hidden field
{{/if}}
And of course the devil is in the implementation details, so, a full working example (with sample data I made up -- we can iterate on this if you want for your specific data set, just leave a comment on this post/answer).
import Component from '#glimmer/component';
import { tracked } from '#glimmer/tracking';
import { on } from '#ember/modifier';
import { get } from '#ember/helper';
// This could be your model data from your route
const DATA = {
fruits: [
'apple', 'banana', 'orange', 'mango',
'watermellon', 'avacado', 'tomato?'
],
veggies: ['cocumber', 'tomato?', 'green bean', 'kale', 'spinach'],
peppers: ['carolina reaper', 'habanero', 'jalapeño']
}
export default class Demo extends Component {
#tracked formData;
get categories() {
return Object.keys(DATA);
}
handleInput = (event) => {
let formData = new FormData(event.currentTarget);
let data = Object.fromEntries(formData.entries());
this.formData = data;
}
handleSubmit = (event) => {
event.preventDefault();
handleInput(event);
}
isSelected = (name, value) => this.formData?.[name] === value;
<template>
<form
{{on 'input' this.handleInput}}
{{on 'submit' this.handleSubmit}}
>
<label>
Food Category<br>
<select name="category" placeholder="Select...">
<option selected disabled>Select a food group</option>
{{#each this.categories as |name|}}
<option
value={{name}}
selected={{this.isSelected "category" name}}
>
{{name}}
</option>
{{/each}}
</select>
</label>
<hr>
{{#let (get this.formData "category") as |selectedCategory|}}
{{#if selectedCategory}}
<label>
{{selectedCategory}}<br>
<select name={{selectedCategory}}>
<option selected disabled>
Select {{selectedCategory}}
</option>
{{#each (get DATA selectedCategory) as |food|}}
<option
value={{food}}
selected={{this.isSelected selectedCategory food}}
>
{{food}}
</option>
{{/each}}
</select>
</label>
{{/if}}
{{/let}}
</form>
<hr>
FormData:
<pre>{{toJson this.formData}}</pre>
</template>
}
const toJson = (input) => JSON.stringify(input, null, 4);
This demo is interactive here, on limber.glimdown.com
Note that the syntax used here is what will be default in the upcoming Polaris Edition of Ember, and is available via ember-template-imports
Update (after comments)
Demo here
I took some liberties with the how the fields are dynamic, because I think this more easily shows the concept asked about in the question: dynamically showing fields in a form.
import Component from '#glimmer/component';
import { tracked } from '#glimmer/tracking';
import { on } from '#ember/modifier';
import { get } from '#ember/helper';
export default class Demo extends Component {
#tracked formData;
handleInput = (event) => {
let formData = new FormData(event.currentTarget);
let data = Object.fromEntries(formData.entries());
this.formData = data;
}
handleSubmit = (event) => {
event.preventDefault();
handleInput(event);
}
<template>
<form
{{on 'input' this.handleInput}}
{{on 'submit' this.handleSubmit}}
>
<div class="grid">
<label>
Name <input type="checkbox" name='hasName'>
</label>
<label>
Email <input type="checkbox" name='hasEmail'>
</label>
<label>
Alias <input type="checkbox" name='hasAlias'>
</label>
<hr>
{{#if (get this.formData 'hasName')}}
<label>
Name
<input type="text" name="name" class="border" />
</label>
{{/if}}
{{#if (get this.formData 'hasEmail')}}
<label>
Email
<input type="email" name="email" class="border" />
</label>
{{/if}}
{{#if (get this.formData 'hasAlias')}}
<label>
Alias
<input type="text" name="alias" class="border" />
</label>
{{/if}}
</div>
</form>
<hr>
FormData:
<pre>{{toJson this.formData}}</pre>
</template>
}
const toJson = (input) => JSON.stringify(input, null, 4);
And... since it seems you have a lot of fields, you may want to go as dynamic as possible:
demo here
which is the following code:
<form
{{on 'input' this.handleInput}}
{{on 'submit' this.handleSubmit}}
>
<div class="grid">
{{#each FIELDS as |field|}}
<label>
{{field}} <input type="checkbox" name='has-{{field}}'>
</label>
{{/each}}
<hr>
{{#each FIELDS as |field|}}
{{#if (get this.formData (concat 'has-' field))}}
<label>
{{field}}
<input type="text" name={{field}} class="border" />
</label>
{{/if}}
{{/each}}
</div>
</form>
I guess Simple js code did the magic of adding and retriving data.. pity of me after finding out.. And for some dynamic ember formdata the previous answer from nullvox helped out.. so here is the code
.hbs
<table class="table">
<th>
<td>Sf</td>
</th>
<th>
<td>Db</td>
</th>
<tbody id = "map">
</tbody>
</table>
<button class = "btn btn-sm btn-primary" type="button" {{action "submit"}}>Submit</button>
<button class = "btn btn-success btn-sm" onclick = {{action "get"}} type="button">Add another</button>
controller code for creating element
#action
get() {
let div = document.getElementById('map');
let tr = document.createElement('tr');
let td = document.createElement('td');
let td2 = document.createElement('td');
var select = document.createElement('select');
select.setAttribute('class', 'sfselect');
div.appendChild(tr);
tr.appendChild(td);
td.appendChild(select);
for (var i = 0; i < sf.length; i++) {
var option = document.createElement('option');
option.value = sf[i];
option.text = sf[i];
select.appendChild(option);
}
var select2 = document.createElement('select');
select2.setAttribute('class', 'dbselect');
tr.appendChild(td2);
td2.appendChild(select2);
for (var i = 0; i < db.length; i++) {
var option = document.createElement('option');
option.value = db[i];
option.text = db[i];
select2.appendChild(option);
}
}
controller code for getting data
#action submit() {
var sfattr = document.querySelectorAll('.sfselect');
var dbattr = document.querySelectorAll('.dbselect');
var sf = [];
var db = [];
console.log(sfattr.length);
let datas;
for (var i = 0; i < sfattr.length; i++) {
sf[i] = sfattr[i].value;
db[i] = dbattr[i].value;
}
let m1 = sf.toString();
let m2 = db.toString();
$.ajax({
url: 'http://localhost:8080/lorduoauth/Map',
method: 'POST',
contentType: 'application/x-www-form-urlencoded',
data: {
m1: m1,
m2: m2,
},
success: function (response) {
console.log(datas);
alert(response);
},
error: function (xhr, status, error) {
var errorMessage = xhr.status + ': ' + xhr.statusText;
alert('error' + errorMessage);
},
});
}
thus the output looks like this

How to prefill data in vue form?

I am using vue form to edit one item, but I cannot conclude in which method should I assign a values to categoryName and description to make those values ​​appear in form?
I tried to make axios call and assign response to categoryName and description in the methods: created, mounted, beforeMouned, but it does not show values of categoryName and description in input fields, although values are obtained from the backend. Also v-model.lazy does not give results.
This is my code:
<template>
<div class="pt-5">
<form #submit.prevent="submitCategory">
<div class="form-group">
<label for="categoryName">Category name</label>
<input v-model.lazy="categoryName" value="categoryName" type="text" class="form-control" id="categoryName" aria-describedby="categoryNameHelp" placeholder="Enter category name">
</div>
<br>
<div class="form-group">
<label for="description">Description</label>
<input v-model="description" type="text" class="form-control" id="description" placeholder="Enter description">
</div>
<br>
<button type="submit" class="btn btn-primary mt-2">Submit</button>
</form>
</div>
</template>
<script>
export default {
name: "EditCategory",
data() {
return {
categoryName: '',
description: '',
}
},
beforeMount () {
this.$axios.get(`/api/categories/${this.$route.params.id}`,{
id: this.$route.params.id
}).then((response) => {
console.log(response.data)
this.categoryName = response.categoryName;
this.description = response.description;
}); },
methods: {
submitCategory() {
this.$axios.post('/api/categories', {
categoryName: this.categoryName,
description: this.description,
}).then(response => {
console.log(response)
this.$router.push({name: 'Categories'});
})
},
initialize () {
},
},
}
</script>
<style scoped>
</style>
Axios Response object
When we send a request to a server, it returns a response. The Axios response object consists of:
data - the payload returned from the server
status - the HTTP code returned from the server
headers - headers sent by server
You did console.log(response.data) in BeforeMount Hook. You will get your required data under response.data object
this.categoryName = response.data.categoryName;
this.description = response.data.description;

Can't clear form/state after input in React.js

I have a form which ultimately will be used as the UI to make some API calls to Open weather map.
Right now when I submit the a zip code in the input field, upon submission [object Object] propagates the field like in the screen shot below.
The call to the API is working as I am getting the JSON for the correct zip code...
But shouldn't this in the handleSubmit take care of everything i.e. using Object.assign to create new state and then using form.zipcode.value = ''; to clear out the input?
Thanks in advance!!
handleSubmit(event) {
event.preventDefault();
var form = document.forms.weatherApp;
api.getWeatherByZip(this.state.zipcode).then(
function(zip) {
console.log('zip', zip);
this.setState(function() {
return {
zipcode: Object.assign({}, zip),
};
});
}.bind(this)
);
form.zipcode.value = '';
}
I have enclosed all of the component's code here.
import React, { Component } from 'react';
import * as api from '../utils/api';
import '../scss/app.scss';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
zipcode: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
zipcode: event.target.value,
});
}
handleSubmit(event) {
event.preventDefault();
var form = document.forms.weatherApp;
api.getWeatherByZip(this.state.zipcode).then(
function(zip) {
console.log('zip', zip);
this.setState(function() {
return {
zipcode: Object.assign({}, zip),
};
});
}.bind(this)
);
form.zipcode.value = '';
}
render() {
return (
<div className="container">
<form name="weatherApp" onSubmit={this.handleSubmit}>
<h2>Open Weather App</h2>
<div className="row">
<div className="one-half column">
<label htmlFor="insertMode">Insert your location</label>
<input
name="zipcode"
className="u-full-width"
placeholder="please enter your zipcode"
type="text"
autoComplete="off"
value={this.state.zipcode}
onChange={this.handleChange}
/>
</div>
<div className="one-half column">
<label htmlFor="showMin">show minimum</label>
<input type="checkbox" />
<label htmlFor="showMax">show maximum</label>
<input type="checkbox" />
<label htmlFor="showMean">show mean</label>
<input type="checkbox" />
</div>
</div>
<div className="row">
<div className="two-half column">
<input type="submit" value="Submit" />
</div>
</div>
</form>
</div>
);
}
}
You should let react manage the changes to the DOM rather that editing it manually. As the value of your input field is already bound to this.state.zipcode to reset it just invoke this.setState({zipcode: ''}) instead of form.zipcode.value='';.

How do you isolate changes in React Props/State

Within the context of Meteor/React I have a table component that subscribes to a Mongo Database, this subscription has a limit parameter that can be updated via props. I'm not sure why but it appears that the componentDidMount Lifecycle function is firing almost continually leading to unpleasant flickering.
The code is quite lengthy but I've attached what I think is the problematic section - more generally, how do you go about isolating what change in state/props is leading to a re-render? I've tried monitoring Chrome Tools but see no change there.
Any feedback or help appreciated!
import React from 'react';
import booksSingleLine from '../booksTable/booksSingleLine';
import TrackerReact from 'meteor/ultimatejs:tracker-react';
export default class booksListingTable extends TrackerReact(React.Component) {
constructor(props) {
super(props);
console.log("Constructor props are" + this.props.LimitProp);
}
componentWillMount() {
console.log("Mounting booksListingTable");
console.log("Will mount props are" + this.props.LimitProp);
this.state = {
}
}
componentDidMount(props){
// console.log("Did mount and props are" +props.LimitProp);
var limit = this.props.LimitProp
limit = parseInt(limit) || 5;
this.state = {
subscription: {
booksData: Meteor.subscribe("allbooks",{sort: {_id:-1}, limit: limit})
}
}
}
componentWillReceiveProps(props) {
console.log("component will get new props " + props.LimitProp);
// Note that for this lifecycle function we have to reference the props not this.props
// console.log("component will get weird props " + this.props.LimitProp);
var limit = props.LimitProp
limit = parseInt(limit) || 5;
this.state = {
subscription: {
booksData: Meteor.subscribe("allbooks", {limit: limit})
}
}
}
componentWillUnmount() {
}
booksDataLoad(){
var filteredCity = this.props.FilterProp;
console.log("filter is " + filteredCity);
if (filteredCity) {
console.log("just passing a few things")
return (
remotebookss.find({location: filteredCity}).fetch()
)
}
else {
console.log("passing everything to table");
return(
remotebookss.find().fetch()
)}}
render() {
return(
<div>
<table className="ui celled table">
<thead>
<tr>
<th onClick ={this.props.HeaderOnClick}>Name</th>
<th>Date</th>
<th>Summary</th>
<th>Site address</th>
<th>Price is</th>
</tr></thead>
<tbody>
{this.booksDataLoad().map( (booksData)=> {
return <booksSingleLine key={booksData._id} booksData={booksData} />
})}
</tbody>
<tfoot>
<tr><th colspan="3">
<div class="ui right floated pagination menu">
<a class="icon item">
<i class="left chevron icon"></i>
</a>
<a class="item" onClick= {this.props.methodLoadMore}>Load More</a>
<a class="icon item">
<i class="right chevron icon"></i>
</a>
</div>
</th>
</tr></tfoot>
</table>
</div>
)
}
}
I would strongly recommend you read the following blog post --
https://www.discovermeteor.com/blog/data-loading-react/
Things I see:
You're resetting state all over the place
You're not managing the subscription as a clear object - your code leaks them (FYI).
Here's a version that uses props and default props to setup your Limit, with that it checks only on startup and change to change the subscription.
import React from 'react';
import booksSingleLine from '../booksTable/booksSingleLine';
import TrackerReact from 'meteor/ultimatejs:tracker-react';
export default class booksListingTable extends TrackerReact(React.Component) {
static propTypes = {
LimitProp: React.PropTypes.number,
}
static defaultProps = {
LimitProp: 5,
}
constructor() {
super();
console.log("Constructor props are" + this.props.LimitProp);
const subscription = Meteor.subscribe("allbooks",{sort: {_id:-1}, limit: this.props.LimitProp})
this.state = {
booksData: subscription,
}
}
componentWillReceiveProps(nextProps) {
console.log("component will get new props " + nextProps.LimitProp);
// Start new subscription - if it's changed
if (this.props.LimitProp != nextProps.limitProp) {
// Stop old subscription
this.state.booksData.stop()
// Setup new subscription
const subscription = Meteor.subscribe("allbooks",{sort: {_id:-1}, limit: nextProps.LimitProp})
this.setState({ booksData: subscription })
}
}
componentWillUnmount() {
// Stop old subscription
this.state.booksData.stop()
}
booksDataLoad(){
var filteredCity = this.props.FilterProp;
console.log("filter is " + filteredCity);
if (filteredCity) {
console.log("just passing a few things")
return (
remotebookss.find({location: filteredCity}).fetch()
)
}
else {
console.log("passing everything to table");
return(
remotebookss.find().fetch()
)
}
}
render() {
return(
<div>
<table className="ui celled table">
<thead>
<tr>
<th onClick ={this.props.HeaderOnClick}>Name</th>
<th>Date</th>
<th>Summary</th>
<th>Site address</th>
<th>Price is</th>
</tr></thead>
<tbody>
{this.booksDataLoad().map( (booksData)=> {
return <booksSingleLine key={booksData._id} booksData={booksData} />
})
}
</tbody>
<tfoot>
<tr><th colspan="3">
<div class="ui right floated pagination menu">
<a class="icon item">
<i class="left chevron icon"></i>
</a>
<a class="item" onClick= {this.props.methodLoadMore}>Load More</a>
<a class="icon item">
<i class="right chevron icon"></i>
</a>
</div>
</th>
</tr></tfoot>
</table>
</div>
)
}
}

Meteor-react inserting url into mongodb stores 'null'

I am using form component to insert data into the Mongo-Collection. When I check in terminal for stored information I do successfuly store Title and data from Select input, but have value of null for both url and file inputs.
Here is the code for handling insert method on import folder:
Meteor.methods({
'posts.insert' : function(post) {
return Posts.insert({
createdAt: new Date(),
title: post.title,
social: post.social,
link: this.link,
file: this.file
});
}
});
Here is the component code for handling form submit:
import React, { Component } from 'react';
class AddPost extends Component {
constructor(props) {
super(props);
this.state = {error: ''};
}
handleSubmit(event) {
event.preventDefault();
const title = this.refs.title.value;
const social = this.refs.social.value;
const link = this.refs.link.value;
const file = this.refs.file.value;
Meteor.call('posts.insert', {title, social, link, file});
}
render() {
return (
<div className="modal fade" id="myModal" tabIndex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div className="form-outer">
<form id='add_post' onSubmit={this.handleSubmit.bind(this)}>
<div className='form-text form-header'>
<p><strong>Hey</strong>, master<span className='error'>{this.state.error}</span></p>
<p>Lets share something new today?</p>
</div>
<input ref="title" type="text" className="form-input" placeholder="What about the title?" />
<div className='form-text form-header form-header-distance'>
<p>Where should I point the way?</p>
</div>
<select ref="social" className="form-select">
<option>Select</option>
<option>Instagram</option>
<option>Twitter</option>
</select>
<input ref="link" type="url" className="form-input" placeholder="Point the way" />
<div className='form-text form-header form-header-distance'>
<p>And what about the image?</p>
</div>
<label className="file form-file">
<input ref='file' className='form-input' type="file" id="file" />
<span className="file-custom"></span>
</label>
<button type="button" className="form-button" data-dismiss="modal">Close</button>
<button type="sumbit" className="form-button" >Save</button>
</form>
</div>
</div>
);
}
}
export default AddPost;
P.S: It's out of topic of these question, but I will do appreciate a lot if you could point me to some external resource or explain if it's possible to upload/store new images (not static from public folder) from local machine and serve them to front-end view?
Make your meteor method as below:
Meteor.methods({
'posts.insert'(post){
post.createdAt: new Date();
Posts.insert(post);
}
})