I have created a repeater, so when user clicks on plus icon then a new row with two input tags is appended. Below is my code :
repeater.js
import React from "react"
const Details = (props) => {
return (
props.Desc !== '' ?
props.Desc.map((val, idx) => {
let desc = ` desc-${idx}`, file = `file-${idx}`
return (
<tr key={val.index}>
<td> Description</td>
<td >
<input type="text" defaultValue={val.desc} name="desc" data-id={idx} id={desc} className="form-control " />
</td>
<td className="mr-2"> Files</td>
<td>
<input type="file" defaultValue={file} name="file" id={file} data-id={idx} className="form-control " />
</td>
<td>
{
idx === 0 ? <button onClick={() => props.add()} type="button" className="btn btn-primary text-center"><i className="fa fa-plus-circle" aria-hidden="true"></i></button>
: <button className="btn btn-danger" onClick={(() => props.delete(val))} ><i className="fa fa-minus" aria-hidden="true"></i></button>
}
</td>
</tr >
)
})
: null
)
}
export default Details
Details.js
import React, { Fragment, Component } from 'react'
import Details from './repeater.js'
class CreateDetail extends Component {
constructor(props) {
super(props);
this.state = {
inputList: [{ index: Math.random(), desc: "", file: "" }],
}
}
onSubmit = (e) => {
console.log('data : ', this.state.inputList[0]);
}
handleChange = (e) => {
if (["desc", "file"].includes(e.target.name)) {
let Desc = [...this.state.inputList]
inputList[e.target.dataset.id][e.target.name] = e.target.value;
} else {
this.setState({ [e.target.name]: e.target.value })
}
}
addNewRow = (e) => {
this.setState((prevState) => ({
inputList: [...prevState.inputList, { index: Math.random(), desc: "", file: "" }],
}));
}
deteteRow = (index) => {
this.setState({
inputList: this.state.inputList.filter((s, sindex) => index !== sindex),
});
}
clickOnDelete(record) {
this.setState({
inputList: this.state.inputList.filter(r => r !== record)
});
}
render() {
let { inputList, flag } = this.state
return (
<Fragment>
<div className="container-fluid">
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<form className="needs-validation" onChange={this.handleChange}>
<div className="form-group row">
<label className="col-xl-3 col-md-3">Details</label>
<div className="col-9 col-md-9">
<table className="table">
<tbody>
<Details add={this.addNewRow} delete={this.clickOnDelete.bind(this)} Desc ={inputList} />
</tbody>
</table>
</div>
</div>
<div className="pull-right">
<button type="button" className="btn btn-primary" onClick={this.onSubmit}> save </button>
</div>
</form>
</div>
</div>
</div>
</div >
</div >
</Fragment >
)
}
}
export default CreateDetail
But I am facing one issue, when I use the input tag's type "file" and I upload an image then I am receiving the fakepath as shown below.
C:\fakepath\6t8Zh249QiFmVnkQdCCtHK.jpg
I am encountering this problem only in repeater. If I use an input tag with type "file", outside the repeater then I am receiving the correct path.
The fake path is the main issue because if I extract the file name and upload it to s3 then empty image is uploaded to s3.
How can I upload a file in repeater?
the browser will not allow getting the local path of the file.you can use the data as form data and store it in state and can send it to S3.
like
handleChange = (e) => {
let formData = new FormData
for (var i = 0; i < e.target.files.length; i++) {
formData.append(e.target.name, e.target.files[i])
}
.....
}
Related
I did looking for but i did not find an answer for my problem.
I need to create a dynamic form with dynamic fields, with the order that the user need.
import { useState } from "react";
const DemoLateral = () => {
const itemDataObject = {
title_item_lateral: '' ,
text_item_lateral: [],
image_lateral: [
{
title_image_lateral: '',
path_image_lateral: '',
}
],
document_lateral: [],
links: [
{
title_link:'' ,
link: ''
}
]
};
const addFields = () => {
let newItemField;
newItemField = itemDataObject;
setItems([...items, newItemField]);
};
const [items, setItems] = useState([]);
const [select, setSelect] = useState([]);
console.log(items);
console.log('select: ', select);
const handleChange = () => {
//let index =
//let name = items[i][e.target.name]=[e.target.value];
console.log();
};
const submitForm = (e) => {
e.preventDefault();
};
console.log(select);
return (
<>
<h3 className="ms-5 mb-5"> AÑADIR ITEMS </h3>
<div className="container">
<form onSubmit={submitForm} className=''>
<div>
{items.map((input, i)=> (
<>
<div className="row align-items-center row mb-4" key={i}>
<label htmlFor="exampleFormControlSelect1">Selecciona el Campo</label>
<div className="col-2" key={i}>
<select className="form-control" id="exampleFormControlSelect1"
onChange={(e) => setSelect([select[i]=e.target.value])} key={i}>
<option>Subtitulo</option>
<option>Imagen</option>
<option>Link</option>
<option>Texto</option>
</select>
</div>
<div className='col-8'>
<input
placeholder="desde From"
id={i}
className='form-control'
value={select[i]}
onChange= {handleChange(i)}
type="text"
required
/>
</div>
<button className="btn btn-danger col-1" >Borrar</button>
</div>
</>
))
}
</div>
<button className="btn btn-success me-4 mt-5" type='submit'>AddSubmit</button>
</form>
<div className="mt-5 text-center">
<button className="btn btn-primary me-4 mb-4" value='items' onClick={addFields}>Add Items</button>
</div>
</div>
</>
);
};
export default DemoLateral;
with this code i try to create a dynamic form with fields, that would be set in the form like the user need:
p.e:
'subtitle'
'image'
'text'
'text'
'link'
'image'
for this i create a select to choose the type of field, and try to put the select in the attribute name, for then when submit all works.
But i can not achieve. :-(
Where is my wrong....
maybe there are other way to do the same kind of form?
I'm having weird problem in React JS. I have two classes named as Notes.js and DataTables.js
I'm using DataTables in Note.js like this
<DataTables
keyField="id"
columns={columns}
url={this.state.url}
useCallBack={true}
onEdit={this.onEdit}
/>
Please Note that DataTables.js is my own custom created DataTable.js not react-datatable.
All the work like fetching data from URL and showing it in tabular form is in DataTables.js file.
Note.js Code:
import React, { Component } from "react";
import { Constant } from "../shared/Constants";
import DataTables from "../shared/DataTables";
import { Modal, Button } from "react-bootstrap";
import BreadCrumb from "../shared/BreadCrumb";
import "../Style.css";
const columns = Constant.notes;
export class Notes extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
url: "notes/get_notes",
showModal: false,
note: [],
};
this.onEdit = this.onEdit.bind(this);
this.onAdd = this.onAdd.bind(this);
this.onUpdate = this.onUpdate.bind(this);
this.saveNote = this.saveNote.bind(this);
}
onUpdate(key, value) {
let noteData = this.state.note;
noteData[key] = value;
this.setState({
note: noteData,
});
}
saveNote(e) {
e.preventDefault();
}
onEdit(n) {
this.setState({
note: n,
showModal: true,
});
}
onAdd() {
this.setState({
note: [],
showModal: true,
});
}
render() {
return (
<>
<Modal
show={this.state.showModal}
aria-labelledby="example-modal-sizes-title-lg"
onHide={() => this.setState({ showModal: false })}
>
<form method="post" onSubmit={this.saveNote}>
<Modal.Header>
<Modal.Title>My Note</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="row">
<div className="col-sm-12">
<div className="form-group">
<label className="text-muted">Note Title</label>
<input
type="text"
placeholder="Note Title"
className="form-control"
ref="title"
value={this.state.note.title}
onChange={(e) => this.onUpdate("title", e.target.value)}
/>
</div>
<div className="form-group">
<label className="text-muted">Content</label>
<textarea
onChange={(e) => this.onUpdate("content", e.target.value)}
className="form-control"
style={{ height: "250px" }}
placeholder="Content"
>
{this.state.note.content}
</textarea>
</div>
</div>
</div>
</Modal.Body>
<Modal.Footer>
<Button
variant="secondary"
onClick={() => this.setState({ showModal: false })}
>
Close
</Button>
<Button type="submit" variant="primary">
Save Note
</Button>
</Modal.Footer>
</form>
</Modal>
<BreadCrumb
title="My Notes"
useCallBack={true}
onAdd={this.onAdd}
active_link="Notes"
link=""
link_text="Add New"
/>
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<div className="card-title">Notes</div>
<DataTables
keyField="id"
columns={columns}
url={this.state.url}
useCallBack={true}
onEdit={this.onEdit}
/>
</div>
</div>
</div>
</div>
</>
);
}
}
export default Notes;
I'm having Problem in Note.js on onUpdate function
onUpdate(key, value) {
let noteData = this.state.note;
noteData[key] = value;
this.setState({
note: noteData,
});
}
Problem: When I update a field in Modal as you can see in my code, then my Table in DataTable.js automatically gets updated, I'don't why :/
Here is DataTables.js function where I'm sending data to onEdit function
const TableData = () => {
return (
<tbody>
{tableData.length === 0 ?
<tr>
<td className="text-center" colSpan="5"><strong>No Data Found</strong></td>
</tr>
:
tableData.map((tData) => (
<tr key={tData[this.props.keyField]}>
{this.props.columns.map((item, index) => (
<td key={index} className="table-content">
{index === 0 ?
[(useCallback === true ? <span key={"sub_"+index} className="link" onClick={() => this.props.onEdit(tData)}>{tData[item.dataField]}</span> :
<Link
to={
this.props.edit_link +
"/" +
tData[this.props.edit_key_first] + (this.props.edit_key_second ? "/" +
tData[this.props.edit_key_second] : '')
}
>
{tData[item.dataField]}
</Link>
)]
: (
tData[item.dataField]
)}
</td>
))}
</tr>
))}
</tbody>
);
};
Please check gif image below so you can understand it :P
You have an onChange function that is updating the content
onChange={(e) => this.onUpdate("content", e.target.value)}
If you don't want to change the content while typing then you will have to remove this.
I'm currently trying to return multiple buttons from a class using Reactjs. Currently I can get all the buttons to return but the onClick function will only work on the last button to be displayed. If anyone can help its greatly appreciated. Here are the important lines of code.
let GroupCollection2 = db.collection('groups');
GroupCollection2.get()
.then(snapshot => {
snapshot.forEach(doc => {
if(doc.get('ModuleCode') === document.getElementById("groupSearch").value)
{
document.getElementById("groupDisplayError").innerHTML = "";
if(found === false){
document.getElementById("groupDisplayTable").innerHTML = '<tr id="groupDisplayTableHeader"><th>Group Name</th><th>Module Code</th><th>Join Or View Group</th></tr>';
}
found = true;
document.getElementById("groupDisplayTable").innerHTML += "<tr><td>"+doc.id+"</td><td>"+doc.data().ModuleCode+"</td><td class='groupDisplayTableButton'></td></tr>";
ReactDOM.render(<Button command={doc.id} />, document.getElementsByClassName('groupDisplayTableButton')[count]);
count++;
}
});
if(found === false){
document.getElementById("groupDisplayError").innerHTML = "No Results.";
}
})
.catch(err => {
console.log('Error getting documents', err);
});
and
class Button extends React.Component{
joinGroup(command){
alert(command);
}
render(){
return(<button onClick={this.joinGroup.bind(this, this.props.command ) }>Join Group</button>);
}
}
The entire code is here:
import React from "react";
import ReactDOM from 'react-dom';
import { compose } from "redux";
import { connect } from "react-redux";
import { signout } from "../store/actions/auth";
import requireAuth from "./hoc/requireAuth";
import firebase from "../services/firebase.js";
import Calendar from "./Planner.js"
//<Calendar />
var db = firebase.firestore();
const Main = ({ signout }) => {
return (
<div id="homePage" className="container">
<div className="row">
<div class="col s6" id="createPage">
<form id="createGroup">
<i className="large material-icons prefix search-icon">group_add</i>
<div className="row">
<div className="col s12">
<div className="row">
<div className="input-field col s12 vert-align">
<input type="text" id="cgroupName" name="groupName"/>
<label htmlFor="cgroupName">Group Name</label>
</div>
</div>
<div className="row">
<div className="input-field col s12 vert-align">
<input type="text" id="cgroupModuleCode" name="moduleCode"/>
<label htmlFor="cgroupModuleCode">Module Code</label>
</div>
</div>
<div className="row">
<input type="button" value="Create Group" onClick={ ()=> createGroup()}/>
</div>
<p id="groupCreateError"></p>
</div>
</div>
</form>
</div>
<div className="col s6">
{/*<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-database.js"></script>*/}
{/* Renders the search bar */}
<i className="large material-icons prefix search-icon">group</i>
<div className="row">
<div className="col s12">
<div className="row">
<div className="input-field col s12 vert-align">
<i className="material-icons prefix search-icon">search</i>
<input type= "text" id ="groupSearch" name="searchGroup"/>
<label htmlFor="groupSearch">Search For A Group</label>
<a className="waves-effect waves-teal btn-flat search-btn" onClick={ ()=> searchGroups()}>Search</a>
</div>
</div>
</div>
</div>
{/* Display any searched groups here */}
<div class="row" id="groupDisplay">
<p id="groupDisplayError"></p>
<table id="groupDisplayTable">
</table>
</div>
</div>
</div>
<button onClick={ () => profile()} hidden>Profile</button>
<button className="btn-switch" onClick={() => signout()}>Log Out</button>
</div>
);
};
function mapStateToProps(state) {
return {
auth: state.firebaseReducer.auth
};
}
function mapDispatchToProps(dispatch) {
return {
signout: () => dispatch(signout())
};
}
function profile(){
}
function logOut(){
document.getElementById("navbar").style.display = "none";
signout();
}
function searchGroups(){
if(document.getElementById("groupSearch").value === ""){
document.getElementById("groupDisplayError").innerHTML = "Please enter a value and try again.";
}
else{
var found = false;
var count = 0;
let GroupCollection = db.collection('groups').doc(document.getElementById("groupSearch").value);
GroupCollection.get()
.then(doc => {
if (doc.exists) {
found = true;
document.getElementById("groupDisplayError").innerHTML = "";
document.getElementById("groupDisplayTable").innerHTML = '<tr id="groupDisplayTableHeader"><th>Group Name</th><th>Module Code</th><th>Join Or View Group</th></tr>';
document.getElementById("groupDisplayTable").innerHTML += "<tr><td>"+doc.id+"</td><td>"+doc.data().ModuleCode+"</td><td class='groupDisplayTableButton'></td></tr>";
ReactDOM.render(<Button command={doc.id}/>, document.getElementsByClassName('groupDisplayTableButton')[count]);
count++;
}
})
.catch(err => {
document.getElementById("groupDisplayError").innerHTML = "Error getting document: "+err;
});
let GroupCollection2 = db.collection('groups');
GroupCollection2.get()
.then(snapshot => {
snapshot.forEach(doc => {
if(doc.get('ModuleCode') === document.getElementById("groupSearch").value)
{
document.getElementById("groupDisplayError").innerHTML = "";
if(found === false){
document.getElementById("groupDisplayTable").innerHTML = '<tr id="groupDisplayTableHeader"><th>Group Name</th><th>Module Code</th><th>Join Or View Group</th></tr>';
}
found = true;
document.getElementById("groupDisplayTable").innerHTML += "<tr><td>"+doc.id+"</td><td>"+doc.data().ModuleCode+"</td><td class='groupDisplayTableButton'></td></tr>";
ReactDOM.render(<Button command={doc.id} />, document.getElementsByClassName('groupDisplayTableButton')[count]);
count++;
}
});
if(found === false){
document.getElementById("groupDisplayError").innerHTML = "No Results.";
}
})
.catch(err => {
console.log('Error getting documents', err);
});
}
}
function createGroup(){
let GroupCollection = db.collection('groups').doc(document.getElementById("cgroupName").value);
GroupCollection.get()
.then(doc => {
if (!doc.exists) {
document.getElementById("groupCreateError").innerHTML = "Group created sucessfully.";
const GroupCollection2 = db.collection('groups');
GroupCollection2.doc(document.getElementById("cgroupName").value).set({
ModuleCode:document.getElementById("cgroupModuleCode").value,
Timetable: "",
User0: firebase.auth().currentUser.email,
User1: "",
User2: "",
User3: "",
User4: "",
User5: "",
User6: "",
User7: "",
User8: "",
User9: "",
})
} else {
document.getElementById("groupCreateError").innerHTML = "Group Name Already Exists.";
}
})
.catch(err => {
document.getElementById("groupCreateError").innerHTML = "Error getting document: "+err;
});
}
class Button extends React.Component{
joinGroup(command){
alert(command);
}
render(){
return(<button onClick={this.joinGroup.bind(this, this.props.command ) }>Join Group</button>);
}
}
export default compose(
connect(
mapStateToProps,
mapDispatchToProps
),
requireAuth
)(Main);
You may have to make sure each button as a unique key. In this case, using the doc.id may work. So, you would want to add a key prop as below:
ReactDOM.render(<Button command={doc.id} key={doc.id} />,
document.getElementsByClassName('groupDisplayTableButton')[count]);
If there's any chance that you will have multiple of the same doc.id, you could modify the key to include the count to ensure each button has a unique key. i.e key={doc.id + count}
I don't think you are binding the joinGroup function properly. Try replacing your Button class with the following functional component:
const Button = ({ command }) => {
const joinGroup = _command => alert(_command);
return (
<button type="button" key={command} onClick={() => joinGroup(command)}>
Join Group
</button>
);
};
Unfortunately, I don't have a solution. That said, I definitely think this line is the problem: ReactDOM.render(<Button command={doc.id} />, document.getElementsByClassName('groupDisplayTableButton')[count]);.
Do not use ReactDOM.render inside the app, render the buttons to their parent, in this case, the table. You can't exactly do that since the table doesn't exist because you are manually adding elements to the DOM.
In the process of trying to figure out what is going on, I ended up refactoring half your code. Thought I'd share it in case it helps at some point.
Here is a sandbox to show that the click handler works with multiple table buttons.
I removed the search function and mocked out firebase.
import React, { useState } from "react";
import ReactDOM from "react-dom";
function App() {
return <Main />;
}
const Main = () => {
// fake firebase mock
const [firebaseMock, setFirebaseMock] = useState("");
// input fields
const [groupName, setGroupName] = useState("");
const [moduleCode, setModuleCode] = useState("");
// error messages
const [errorMessage, setErrorMessage] = useState("");
const createGroup = (_groupName, _moduleCode) => {
// ensure user submist a group name and a module code
if (!_groupName || !_moduleCode) {
setErrorMessage("You must provide a group name and a module code.");
return;
}
// if group already exists with the same name, create an error message
if (Object.keys(firebaseMock).includes(_groupName)) {
setErrorMessage("Group Name Already Exists.");
return;
}
setFirebaseMock({
...firebaseMock,
[_groupName]: { ModuleCode: _moduleCode }
});
// clear inputs
setGroupName("");
setModuleCode("");
};
return (
<div id="homePage" className="container">
<div className="row">
<div class="col s6" id="createPage">
<form id="createGroup">
<i className="large material-icons prefix search-icon">group_add</i>
<div className="row">
<div className="col s12">
<div className="row">
<div className="input-field col s12 vert-align">
<input
type="text"
id="cgroupName"
name="groupName"
value={groupName}
onChange={e => setGroupName(e.target.value)}
/>
<label htmlFor="cgroupName">Group Name</label>
</div>
</div>
<div className="row">
<div className="input-field col s12 vert-align">
<input
type="text"
id="cgroupModuleCode"
name="moduleCode"
value={moduleCode}
onChange={e => setModuleCode(e.target.value)}
/>
<label htmlFor="cgroupModuleCode">Module Code</label>
</div>
</div>
<div className="row">
<input
type="button"
value="Create Group"
onClick={() => createGroup(groupName, moduleCode)}
/>
</div>
<p id="groupCreateError">{errorMessage}</p>
</div>
</div>
</form>
</div>
<div class="row" id="groupDisplay">
{!!Object.keys(firebaseMock).length && (
<table id="groupDisplayTable">
<thead>
<tr id="groupDisplayTableHeader">
<th>Group Name</th>
<th>Module Code</th>
<th>Join Or View Group</th>
</tr>
</thead>
<tbody>
{Object.keys(firebaseMock).map(groupId => (
<tr>
<td>{groupId}</td>
<td>{firebaseMock[groupId].ModuleCode}</td>
<td class="groupDisplayTableButton">
<Button command={groupId} />
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
</div>
);
};
const Button = ({ command }) => {
const joinGroup = _command => console.log(_command);
return (
<button type="button" key={command} onClick={() => joinGroup(command)}>
Join Group
</button>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
The only difference here is that none of my functions contains HTML, they just manipulate state. In the future, a simple way to think in react is to always make sure that HTML code only lives in the render function. That way everything else only manipulates data and state. This decoupling of the soul of an app (data) from the body of app (HTML)lets the HTML 'react' to changes in state, which is the whole point of the framework.
I am new in reactJS.
Here, I make a file in which I have made a form and table.
I have added some static data in to the table already. But, I want to add, update and delete rows dynamically.
How can I achieve this?
Here is my code In which the insert, update and delete function are there.
What will be the correct code for that? Thanks in advance.
import React, { Component } from 'react';
import classes from './Product.css';
import { Link } from 'react-router-dom';
class Product extends Component {
state = {
name: '',
category: '',
price: '',
formErrors: ''
}
handleUserInputs = (e) => {
const name = e.target.name;
const value = e.target.value;
this.setState({ [name]: value });
}
insertHandler = (e) => {
if (this.state.name === '' || this.state.name === null) {
this.setState({ formErrors: 'Blank Name' });
}
else if ((this.state.category === '' || this.state.category === null)) {
this.setState({ formErrors: 'Blank Category' });
}
else if (this.state.price === '' || this.state.price === null || typeof this.state.price === 'undefined') {
this.setState({ formErrors: 'Blank Price' });
}
else if (!this.state.price.match(/^[0-9\b]+$/)) {
this.setState({ formErrors: 'Price should be in digits only.' });
}
else {
// insert code will be here
}
e.preventDefault();
}
editHandler = (e) => {
// update code will be here
}
deleteHandler = (e) => {
// delete row code will be here.
}
render() {
const data = [
{ "id": 1,
"name": "Chips",
"category": "Food",
"price": "20"
},
{
"id": 2,
"name": "Shirt",
"category": "Clothes",
"price": "500"
},
{
"id": 3,
"name": "Mobile",
"category": "Electronics",
"price": "10000"
}
];
return (
<div className={classes.main}>
<div className={classes.Product}>
<div className={classes.product_box_body}>
<form className={classes.form} onSubmit={this.handleSubmit}>
<h2>Add Product</h2>
<div className={classes.name}>
<input type="text" name="name" className={classes.form_control} placeholder="Product Name" value={this.state.name} onChange={(e) => this.handleUserInputs(e)} />
</div>
<div className={classes.category}>
<select className={classes.form_control} name="category" onChange={(e) => this.handleUserInputs(e)} aria-required="true">
<option value="">-- Select any Category --</option>
<option value="Food">Food</option>
<option value="Electronics">Electronics</option>
<option value="Clothes">Clothes</option>
</select>
</div>
<div className={classes.price}>
<input type="text" name="price" className={classes.form_control} placeholder="Price" value={this.state.price} onChange={(e) => this.handleUserInputs(e)} />
</div>
<p className={classes.text_red}>{this.state.formErrors}</p>
<button type="submit" className={classes.product} onClick={(e) => this.insertHandler(e)}>Insert</button>
<Link to="/login" className={classes.cancel}>Cancel</Link>
</form>
</div>
</div>
<div>
<table className={classes.table}>
<thead>
<tr>
<th style={{height: '50px'}}>Name</th>
<th style={{height: '50px'}}>Category</th>
<th style={{height: '50px'}}>Price</th>
<th style={{height: '50px'}}>Action</th>
</tr>
</thead>
<tbody>
{data.map(obj => {
return (
<tr>
<td style={{width: '50px'}}>
{obj.name}
</td>
<td style={{width: '50px'}}>
{obj.category}
</td>
<td style={{width: '50px'}}>
{obj.price}
</td>
<td style={{width: '120px'}}>
<button type="submit" name="Edit" value="edit" onClick={(e) => this.editHandler(e)}>Edit</button> <button type="submit" name="Delete" value="delete" onClick={(e) => this.deleteHandler(e)}>Delete</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
);
}
}
export default Product;
you need to manage your array data in the state, you need :
state={data=[]} and then your delete function erase data of you array and then you need to call the method setState who render de component with the new data merge and the same to update , you modify your array data and later call setState and magic happend, you can asing your data to state in the constructor o componentDidMount method
I have dynamically generated component with textfields and buttons. Each button do the ajax request. It's all working fine. However, I want to display the success message or error message on button itself, adding some icon on it. This is where I got stuck. I setup the flag and change the state, but it will change on all the buttons as expected. I also tried to change the current target, but the reference didn't work in success callback. Can someone please help me with this.
const FormGroup = ({index, type, field, value, onChange, spinner, isLoading, error, buttonType, brandList, handleBrandConfiguration, checkAvailability, handleCaseType, options, handlerRemoveItem})=> {
return(
<div>
<div className="form-group">
<label>{index}</label>
<input type="text"
name={field}
className="form-control"
value={value}
onChange={onChange}
/>
<select className="form-control" defaultValue="" onChange={handleBrandConfiguration}>
<option value="">Please select brand</option>
{brandList}
</select>
<select className="form-control" defaultValue="" onChange={handleCaseType}>
<option value="">Please select case template</option>
{options}
</select>
<button
type={buttonType}
className={classname(isLoading ? error ? "button button-danger" : "button button-success" : error ? "button button-danger" : "button button-primary")}
onClick={checkAvailability}>
<i className={classname(spinner ? error ? '': "fa fa-spinner fa-spin": '')}></i> {isLoading ? error ? 'Not Found' :<i className="fa fa-check fa-2" aria-hidden="true"></i> : error ? 'Not Found': 'Check Availability'}</button>
<input
type="button"
className="button button-danger"
value="Remove Item"
onClick={handlerRemoveItem}/>
</div>
</div>
);
};
Thanks
If you move your checkAvailability and result of api request to FormControl component you can set error and success for single component.
For Example:
class FormGroup extends React.Component{
constructor(props) {
super(props);
this.state = {
availabilityResult: null
};
}
callApi(username){
return(axios.get('https://api.github.com/users/' + username));
}
onChange(e){
this.setState({
itemCode: e.target.value
});
}
checkAvailability(e){
const username = this.state.itemCode;
let currentValue = e.currentTarget.value;
//ajax call goes here
this.callApi(username).then(response => {
const login = response.data.login;
this.setState({
availabilityResult: (login === 'mesmerize86')
});
}).catch(err => {
console.log(err);
});
}
render() {
const {index, field, value, handleRemoveItem} = this.props;
let inputState = '';
if (this.state.availabilityResult === true) inputState = 'button-success';
else if (this.state.availabilityResult === false) inputState = 'button-danger';
return(
<div className="form form-inline">
<div className="form-group">
<label>{index}</label>
<input type="text"
name={field}
className="form-control"
value={value}
onChange={this.onChange.bind(this)}
/>
<input
type="button"
className={`button button-primary ${inputState}`}
value="Check Availability"
onClick={this.checkAvailability.bind(this)} />
<input
type="button"
className="button button-danger"
value="Remove Item"/>
</div>
</div>
)
}
}
class Main extends React.Component {
constructor(props){
super(props);
this.state = {
rowList : [],
itemCodes: [],
itemCode: ''
}
}
handlerAddRow(){
const rowList = this.state.rowList.concat(FormGroup);
this.setState({ rowList });
}
handleRemoveItem(){
console.log('remove Item');
}
render(){
const rowList = this.state.rowList.map((row, index) => {
return (<FormGroup key={index} index={index+1} field={`"itemCode_"${index}`} />);
});
return(
<div className="container">
<input type="button" value="Add a row" className="button button-primary" onClick={this.handlerAddRow.bind(this)} /> <i class="fa fa-spinner" aria-hidden="true"></i>
{rowList}
</div>
);
}
}
ReactDOM.render(<Main />, document.getElementById('app'));