add row on button press React - javascript

im trying to add a row to a table, on a click event. Here is my component:
import React, { Component } from 'react';
import PageContent from 'components/PageContent';
import { IBox, IBoxTitle, IBoxContent } from 'components/IBox';
import IBoxTools from 'components/IBoxTools';
import Table from 'components/Table';
export default class SalesChannelsPage extends Component {
constructor(props) {
super(props);
this.addRow = this.addRow.bind(this);
}
render() {
return (
<PageContent header="Salgskanaler">
<IBox>
<IBoxTitle>
Salgskanaler
<IBoxTools icon="fa fa-plus" title="Tilføj salgskanal" handleClick={() => this.addRow()}/>
</IBoxTitle>
<IBoxContent>
<Table>
<thead>
<tr>
<td className="channel-name">Navn</td>
<td className="channel-description">Beskrivelse</td>
<td className="channel-btn"></td>
</tr>
</thead>
<tbody>
</tbody>
</Table>
</IBoxContent>
</IBox>
</PageContent>
);
}
addRow() {
console.log('add row')
}
}
So everytime i click the button, a new row should be added, and it should be possible to add as many rows to the list as possible. here is the row i want to add.
i realize that i could make an array in state and just put it there, but i have learned that only the data should be contained in the state. Some help would be much appreciated.
Thx
<tr>
<td className="channel-name">
<input className="form-control input-sm" type="text" placeholder="Navn..." />
</td>
<td className="channel-description">
<input className="form-control input-sm" type="text" placeholder="Beskrivelse..." />
</td>
<td className="channel-btn">
<div>
<div className="btn btn-xs btn-danger"><span className="fa fa-times"></span></div>
<div className="btn btn-xs btn-primary"><span className="fa fa-floppy-o"></span></div>
</div>
</td>
</tr>

So as Matthew Herbst said, this is what state is for. Presumably those rows need to display some kind of data. You shouldn't be storing the HTML/JSX in the array, but you should be storing the data used to construct the list in the array. This is what is great about React. You declare how the UI should be presented based on the underlying data. Then you just manipulate the data. So first you need an array in your state that represents the list. Also, you don't need to declare your addRow handler as being returned by a function. It is a function. Just pass the function without invoking it by excluding the () parentheses. Finally, you map over the array, returning the rows. Obviously this is all kind of dump without data, but it is immediately clear what data you want in the rows. So it should look something like this:
import React, { Component } from 'react';
import PageContent from 'components/PageContent';
import { IBox, IBoxTitle, IBoxContent } from 'components/IBox';
import IBoxTools from 'components/IBoxTools';
import Table from 'components/Table';
export default class SalesChannelsPage extends Component {
constructor(props) {
super(props);
this.addRow = this.addRow.bind(this);
this.state = {
rows: []
}
}
render() {
return (
<PageContent header="Salgskanaler">
<IBox>
<IBoxTitle>
Salgskanaler
<IBoxTools icon="fa fa-plus" title="Tilføj salgskanal" handleClick={this.addRow}/>
</IBoxTitle>
<IBoxContent>
<Table>
<thead>
<tr>
<td className="channel-name">Navn</td>
<td className="channel-description">Beskrivelse</td>
<td className="channel-btn"></td>
</tr>
</thead>
<tbody>
{this.state.rows.map(row => <tr></tr>)}
</tbody>
</Table>
</IBoxContent>
</IBox>
</PageContent>
);
}
addRow() {
var nextState = this.state;
nextState.rows.push('placeholder');
this.setState(nextState);
}
}
Again I am just pushing the text placeholder to the end of the array each time because I don't know what data you want in there. But this will keep generate empty <tr> tags for you on each push of the button.

Related

How do I dynamically generate table rows without jquery?

Currently, I have a table class as follows:
import React from "react";
import "./Table.css";
export default function Table({theadData, tbodyData}) {
return (
<>
<table>
<tr>
<th></th>
<th>2017</th>
</tr>
{Array.from(theadData).forEach(heading => {
<tr>
<td class="largeHeader" key={heading}>{heading}</td>
<td class="primaryCell">{tbodyData[heading].value}</td>
</tr>;
})}
</table>
</>
);
}
When I add console.log(heading) or console.log(tbodyData[heading].value) within the loop, I can see that they give the expected values. However, none of the rows are added on. Why is that and how can I solve this problem? (I'd prefer to avoid jquery and libraries of that nature if possible, but am open to ideas.)
There are several mistakes you made:
change forEach to map
replace {} with (), or add return before <tr>
put key on the root element which is <tr>
{Array.from(theadData).map(heading => (
<tr key={heading}>
<td className="largeHeader">{heading}</td>
<td className="primaryCell">{tbodyData[heading].value}</td>
</tr>
))}

Getting error "props.data.map is not a function" in React

I am trying to make a form where on submit the data will be displayed in the table below. I have created Form and Table components and passing the form data into Table component. In the table component I have created 2 components; TableHeader and TableBody and passing the data to TableBody from Table. However, I am getting an error:
props.data.map is not a function
Here is my code:
Form.js
import React, { Component } from 'react';
import DisplayTable from '../Components/Table';
import Jumbotron from 'react-bootstrap/Jumbotron';
import Container from 'react-bootstrap/Container';
class Form extends Component {
constructor(){
super();
//1st 3 state fields will fetch the input data and next 3 will store the updated state into it
this.state={
first:'',
last:'',
email:'',
firstU:'',
lastU:'',
emailU:''
};
this.onInputChange=this.onInputChange.bind(this);
this.onInputSubmit=this.onInputSubmit.bind(this);
}
//on input change captures the values of the fields into respective input fields (first,last,email)
onInputChange=(event)=>{
this.setState({
[event.target.name]:event.target.value
}
)
//console.log(event.target.value);
// console.log('input change running');
}
//sets the values on submit
onInputSubmit=()=>{
// this.setState();
//console.log(this.state.first);
this.submitData(this.state.first,this.state.last,this.state.email);
//console.log('input submit running');
}
//sets the onChange state into new state fields
submitData=(first,last,email)=>{
this.setState({
firstU:first,
lastU:last,
emailU:email
});
console.log('submitData running');
}
render() {
return (
<Jumbotron fluid>
<Container>
<h1>Form</h1>
<div className="App" align="center">
<div class="row">
<input
name="first"
onChange={this.onInputChange}
type="text"
value={this.state.first}
/>
</div>
<div class="row">
<input
name="last"
onChange={this.onInputChange}
type="text"
value={this.state.last}
/>
</div>
<div class="row">
<input
name="email"
onChange={this.onInputChange}
type="email"
value={this.state.email}
/>
</div>
<div class="row">
<button name="submit" onClick={this.onInputSubmit}>
Submit
</button>
</div>
</div>
{/*here passed entire state into single variable instead of passing like
this.state.first,this.state.last to reduce code
*/}
<DisplayTable data={this.state}/>
</Container>
</Jumbotron>
);
}
}
export default Form;
Table.js
import React, { Component } from 'react';
import Jumbotron from 'react-bootstrap/Jumbotron';
import Container from 'react-bootstrap/Container';
import Table from 'react-bootstrap/Table';
//props.data will fetch value from the state
class DisplayTable extends Component{
render(){
//console.log("hi"+this.props.data.first);
//const data=this.props;
return (
<Jumbotron fluid>
<Container>
<h1>Data</h1>
<TableHeader/>
<TableBody data={this.props}/>
</Container>
</Jumbotron>
);
}
}
const TableHeader= ()=> {
return(
<div>
<Table striped bordered hover variant="dark">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>email</th>
</tr>
</thead>
</Table>
</div>
);
}
const TableBody = (props)=> {
console.log("k"+props.data.first)
const rows=props.data.map((row,index)=>{
return(
<div>
<Table striped bordered hover variant="dark">
<tbody>
<tr>
<td key={index}></td>
<td>{row.first}</td>
<td>{row.last}</td>
<td>{row.email}</td>
</tr>
</tbody>
</Table>
</div>
);
})
return ({rows});
}
export default DisplayTable;
A good way to go about solving this problem might be to edit your state like so:
this.state = {
users: [
{
first: 'name',
last: 'name',
etc...
},
{
first: 'name',
last: 'name',
etc...
}
],
other: state //if need be
}
This way, you have an array of users and can add more users to the table as needed.
Then you just pass this.state.users to your child component through props.
Is your props.data is an array because map works with array only. I think your data is in {} but not in []. Please verify.
print the data to verify it.

Map through an array that creates a horizontal table

I am trying to map through an array and dynamically create a horizontal table, much like the image below here.
I am mapping through an array like so,
const glanceGames = this.state.gameData.map(game => {
return <GameTable
key={game.id}
home_team_name={game.home_name_abbrev}
away_team_name={game.away_name_abbrev}
home_score={game.linescore.r.home}
away_score={game.linescore.r.away}
status={game.status.status}
/>
})
and then using that data to populate the following component.
const GameTable = (props) => {
return (
<div>
<table>
<thead>
<tr>
<th>{props.status}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{props.home_team_name}</td>
<td>{props.home_score}</td>
</tr>
<tr>
<td>{props.away_team_name}</td>
<td>{props.away_score}</td>
</tr>
</tbody>
</table>
</div>
)
}
However, the output is a vertical table rather than a horizontal one. I feel like this should be easy, yet I keep running into issues. Anu suggestions would be helpful! I am using React.
I don't think this is nothing to do with react, we can just do it with css:
...
render(){
<div style={{display: 'flex'}}>
{glanceGames}
</div>
}

How to sort API response for some field

I just start to learn Reactjs and have stopped with some question.
I've fetched data through axios for some API endpoint(get countries with some data like population,currencies,region etc.) and I want to sort data for some field(region or population for example).
I thought that in reactjs it will be simple to do and reactjs have some built-in things to do that. But when I've tried to do that and when I've searched answer in google I was really exposed. What is the best way to implement sorting? How can I do that?
What I've tried:
import React, { Component } from 'react';
import axios from 'axios';
import './App.css';
class App extends Component {
constructor(props){
super(props)
this.state={
countries:[],
}
}
componentDidMount(){
axios.get('https://restcountries.eu/rest/v2/all')
.then(response => {
this.setState({
countries:response.data
})
})
.catch((error) => {
console.log("error", error)
})
}
render(){
return(
<div className = 'table'>
<table>
<tbody>
<tr>
<th>Country name </th>
<th>Currencies</th>
<th>Region </th>
<th>Flag </th>
<th >Population</th>
<th>Languages</th>
</tr>
{
this.state.countries.map((country,id) => {
return(
<tr className="tableRow">
<td> {country.name}</td>
<td>{country.currencies.map((currency,i)=>{
return (
<p>
<span>{currency.name} </span>
<span>{currency.symbol} </span>
</p>
)
})} </td>
<td> {country.region}</td>
<td> <img src={country.flag} alt={country.denonym}/> </td>
<td> {country.population} </td>
<td> {country.languages.map((language)=>{
return (
<span> {language.name} </span>
)
})} </td>
</tr>
)
})
}
</tbody>
</table>
</div>
)
}
}
export default App;
Basically what you need to do is something like this:
...
this.state.countries.sort(/* Provide sort function here */).map((country,id) => {
...
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Issue with delete functionality ReactJS: TypeError: Cannot read property 'removeProduct' of undefined

I got the products that comes with the props mapped to show in a table, but when I click the button I can't reach the method removeProduct(product) with the onClick method within the button. I'm getting an error like follow:
TypeError: Cannot read property 'removeProduct' of undefined
at ProductsDashboard.js:53
at immutable.js:3018
at List.__iterate (immutable.js:2208)
at IndexedIterable.mappedSequence.__iterateUncached (immutable.js:3017)
at seqIterate (immutable.js:606)
at IndexedIterable.IndexedSeq.__iterate (immutable.js:322)
at IndexedIterable.toArray (immutable.js:4260)
at new List (immutable.js:2067)
at reify (immutable.js:3572)
at List.map (immutable.js:4403)
//Code of ProductsDashboard.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadProducts, removeProduct } from '../../actions/products_actions';
import { bindActionCreators } from 'redux';
class ProductsDashboard extends Component {
componentWillMount() {
this.props.loadProducts();
}
removeProduct(product) {
this.props.removeProduct(product);
}
render() {
if (!this.props.products) {
return <div>Producten worden geladen</div>;
}
return (
<div>
<a href="#/dashboard/products/add" class="btn btn-sm btn-success pull-right" style={{marginBottom: '20px'}}>Product toevoegen</a>
<div style={{marginBottom: '20px'}}>
<input class="form-control" type="text" placeholder="Zoek product" />
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed">
<thead>
<tr>
<td>Id</td>
<td>Barcode</td>
<td>Naam</td>
<td>Omschrijving</td>
<td>Prijs</td>
<td>Volume</td>
<td>Eenheid</td>
</tr>
</thead>
<tbody>
{ this.props.products.map(function(product, index) {
var editUrl = `#/dashboard/products/edit/${product.get('id')}`;
return(
<tr key={index}>
<td>{product.get('id')}</td>
<td>{product.get('barcode')}</td>
<td>{product.get('name')}</td>
<td>{product.get('description')}</td>
<td>{product.get('price')}</td>
<td>{product.get('volume')}</td>
<td>{product.get('unit')}</td>
<td>Wijzigen</td>
<td><button onClick={ this.removeProduct.bind(null, product) } class="btn btn-sm btn-danger">Verwijderen</button></td>
</tr>
)
})
}
</tbody>
</table>
</div>
</div>
)
}
}
function mapDispatchToPorps(dispatch) {
return bindActionCreators({ loadProducts, removeProduct }, dispatch);
}
function mapStateToProps(state) {
return {
products: state.products.get('products')
}
}
export default connect(mapStateToProps, mapDispatchToPorps)(ProductsDashboard);
this inside the callback of Array.prototype.map is undefined unless specified otherwise:
If a thisArg parameter is provided to map, it will used as callback's
this value. Otherwise, the value undefined will be used as its this
value. The this value ultimately observable by callback is determined
according to the usual rules for determining the this seen by a
function.
Source
To resolve this, pass a value for thisArg:
{ this.props.products.map(function(product, index) {
var editUrl = `#/dashboard/products/edit/${product.get('id')}`;
return(
<tr key={index}>
<td>{product.get('id')}</td>
<td>{product.get('barcode')}</td>
<td>{product.get('name')}</td>
<td>{product.get('description')}</td>
<td>{product.get('price')}</td>
<td>{product.get('volume')}</td>
<td>{product.get('unit')}</td>
<td>Wijzigen</td>
<td><button onClick={ this.removeProduct.bind(null, product) } class="btn btn-sm btn-danger">Verwijderen</button></td>
</tr>
)
}, this)}

Categories