I am building an app to help me learn react and solidity. I have a table which lists available products on the site. I want customers to be able to input the number of items they would like to buy for each product (each row) separately.
It works if I make all of them change a single variable in my state but stylistically I would like each row to have a unique value. To solve this I created an array to hold each value ( the array is called inputs). The tough part here is that the number of products (rows in the table) can vary so I cant just hard code it.
Rendering the table:
<tbody className="has-text-black-bis">
{indices.map((a) => (
<tr className="rows">
<td ><strong>{this.state.itemNames[a]}</strong></td>
<td ><strong>{this.state.costs[a]} Wei</strong></td>
<td ><strong>{this.state.quantities[a]}</strong></td>
//This row! <td >
Qty: <input type="number" className='table-input' name="inputs" value={this.state.inputs[a]} onChange={()=>this.handleTableInput(a)} />
<button type="button" className='buy-btn' onClick={()=>this.buyItem(a)}> Buy!</button>
</td>
</tr>
))}
</tbody>
Handling the input changes normally -This did not work when passing this.state.inputs[a] as the value as other rows would change as well:
handleInputChange = (event) => {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
I thought to change the above function to something like this where a (my index) is passed into the function. The problem is that I am not sure how to access the 'event' types like in the above generic function and thus not sure how to access the value that the user has input.
handleTableInput = (ind) => {
const {inputs} = this.state;
inputs[ind] = "value from table input";
this.setState({inputs: inputs})
}
Thank you for any help!
I am new to vue.js. I was trying to filter the table columns using multiple search fields. I have different search fields and their respective v-models for different columns in the table. I want to enable search for each column
I tried the following solutions:
1. Vue.js filterBy to search in multiple fields
2. How do I search through multiple fields in Vue.js 2
Using the above two solutions I came up with this:
<p>
<label>First Name</label>
<input type="text" placeholder="Enter firstname" v-model="First" />
</p>
<p>
<label>Last Name</label>
<input type="text" placeholder="Enter the last Name" v-model="Last" />
</p>
<p>
<label>CustomerId</label>
<input type="text" placeholder="Enter the CustomerId" v-model="CustomerId" />
</p>
<table id="clients">
<thead>
<tr>
<th width="40%">First Name</th>
<th width="20%">Last Name</th>
<th width="20%">CustomerId</th>
</tr>
</thead>
<tbody v-for="customer in filteredCustomers.slice(0,20)">
<tr>
<td>{{customer.first}}</td>
<td>{{customer.last}}</td>
<td>{{customer.id}}</td>
</tr>
</tbody>
</table>
Script:
export default {
props:['showMod'],
data() {
return {
id: '',
First: '',
Last: '',
CustomerId: '',
categories: []
}
},
computed: {
filteredCustomers: function () {
var self = this;
return this.categories.filter(function (cust) {
return cust.first.toLowerCase().indexOf(self.First.toLowerCase()) >-1 || cust.last.toLowerCase().indexOf(self.Last.toLowerCase()) >-1
});
}
I change the code to enable search on only one field, it works but stops working when trying to search on multiple fields.
I don't know what I am doing wrong in this.
Edit:
I was able to get those search working independently by adding "if"
filteredCustomers: function () {
var self = this;
return this.categories.filter(function (cust) {
if (self.First) {
return cust.first.toLowerCase().indexOf(self.First.toLowerCase()) > -1
}
if (self.CustomerId) {
return cust.clientId.toLowerCase().indexOf(self.CustomerId.toLowerCase()) > -1
}
return cust.last.toLowerCase().indexOf(self.Last.toLowerCase())>-1
});
Hoewever, I could not get two filter conditions working at the same time like first search based on first name and then search on last name.
You just need to adjust the logic:
filteredCustomers() {
return this.categories.filter(customer => {
return (!this.First || customer.first.toLowerCase().includes(this.First.toLowerCase()))
&& (!this.Last || customer.last.toLowerCase().includes(this.Last.toLowerCase()))
});
You could also use regular expressions and .match() if you didn't want to bother with the lower case conversion.
I am trying to create a Table using React and React-Bootstrap that has a custom number of table rows. The table is supposed to store data about player statistics of a certain video game, and based on the video game the statistics may change, thus the number of rows and titles of these rows must be able to dynamically change as well. I wanted to create an array in the state that held the list of current statistics, then map this array to a element using the map function and render the table. However, after trying several approaches I can't get any of the custom input to render. Below is the code :
Class Structure
class Statistics extends Component {
constructor(props) {
super(props)
this.state = {
game: '',
player_names: [],
positions: [],
stat_categories: [
'kills',
'deaths',
'assists'
]
}
}
renderTableRows(array) {
return (
<tr>
<th> NAME </th>
<th> TEAM </th>
<th> POSITION </th>
{ array.map(item => {
console.log(item)
<th key={item}> {item} </th>
})
}
</tr>
)
}
render() {
const columnLength = this.state.player_names.length
const statCols = this.state.stat_categories
return (
<div>
<MyNav url={this.props.location.pathname} />
<Table responsive striped bordered hover>
<thead>
{ this.renderTableRows(statCols) }
</thead>
</Table>
</div>
)
}
}
The console also properly logs the data in state (kills, deaths, assists) -- so the issue is when rendering the element. Any help would be appreciated!
You have no return statement in your map function, inside of renderTableRows.
When using ES6 arrow functions, you can either:
Return data directly without a return statement
(args) => (returnedData);
Or add some logic instead of just returning directly,
(args) => {
// Logic here
return returnedData
}
In the second case you'll need a return statement, because you are logging, if you choose to remove logging, go the first way.
Also, please post the code directly in your question, as using an image makes it less readable and not indexed by search engines.
You have to render each item in separate trs, not as a series of ths
renderTableCols(array) {
return array.map(item => <th>{item}</th>)
}
renderTableColValues(item, cols) {
return cols.map(col => <td>{item[col]}</td>)
}
renderTableRows(array) {
return array.map(item =>
<tr>
<td>{item.name}</td>
<td>{item.team}</td>
<td>{item.position}</td>
{this.renderTableColValues(item, this.cols)}
</tr>
);
}
render() {
return (
<Table>
<thead>
<tr>
<th>NAME</th>
<th>TEAM</th>
<th>POSITION</th>
{this.renderTableCols(this.cols)}
</tr>
</thead>
<tbody>
{this.renderTableRows(items)}
</tbody>
</Table>
);
}
More on tables https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table
I will give you a similar answer of what youre encoutering but its kinda different approach with a excelent solution
So, you are trying to create a dynamic table but youre making table rows static, what i did was letting the table to receive arrays of head and data and then create as many rows or datas that are required.
heres the code
export function objectIntoTableData(object) {
return Object.values(object).map((data, index) => {
return <td key={index}>{data}</td>;
});
}
You must change this index to (value,index) => , thats just my use
tableRows(data) {
return data.map(value => {
return <tr key={value.index}>{objectIntoTableData(value)}</tr>;
});
}
<thead>
<tr>
{head.map((value, index) => {
return <th key={index}>{value}</th>;
})}
</tr>
</thead>
<tbody>{this.tableRows(data)}</tbody>
Rather use a id or index inside your object since the index callback of the map function, its unsafe to use for the keys.
<ReactTableUse
head={["#", "Cell1", "Cell2", "Cell3"]}
data={[{id:1, test:1},{id:2, test:2}]}
/>
Rules:
When your state changes, render method of a class based component will be called.
Question: Who will change the state? will it grow inside the component ? What is your problem ? your are not being able to render anything ? or statistics is not dynamically rendering ? if you want to change it dynamically , you need to change the state first.
I am trying to make table cell editable after clicking on icon in another cell , for that I need to get index of element so the editor will open in the correct row , which icon belongs to.
My issue is that I dont know the way i should get the prop value of table DOM element here is code for for clearify
a part of dom tree generated with react:
<tbody>
{stepsDone.map(function(step,idx) {
let content = step;
const editing = this.state.editing;
if(editing){
content = (
<form onSubmit={this._save}>
<input type="text" defaultValue={step} />
</form>
);
}
return(
<tr key={idx}>
<td className="step" data-step={'step'+idx}>{content}</td>
<td className="icRow">
<Icon className="edit" onClick={this._showEditor} rownum={idx}/>
<Icon className="remove"/>
<Icon className="trash outline"/>
</td>
</tr>
)
},this)}
show editor function:
_showEditor(e){
this.setState({
editing:{
row:e.target.rownum
}
});
console.log(this.state.editing);
}
After execution of showedtior function console logs :
first click = null , which is normal i think
more clicks = undefined , and thats whats brings a trouble i want to receive idx from map function.
here is code from Icon.js
import React from 'react';
import classNames from 'classnames';
export function Icon(props) {
const cssclasses = classNames('icon', props.className);
return <i className={cssclasses} onClick={props.onClick}/>;
}
if you want to reveive the idx from the map function you should pass it to the function _showEditor so your code must be like this :
<Icon className="edit" onClick={this._showEditor(idx)}/>
and the function definition should be :
_showEditor = (idx) => (event) => {
this.setState({
editing:{
row:idx
}
});
console.log(this.state.editing);
}
or if you don't want to use the arrow functions for some reason, just replace
onClick={this._showEditor(idx)}
with
onClick={this._showEditor.bind(this,idx)}
and its definition becomes
_showEditor(idx){...}
What's the best way to accomplish something like this. I have a credit note that I wish to edit the unit price. I'll pull in the invoice details then the user can edit/change the unit price. When I do this, all fields are changing. Somehow I need to put this in its own state? I have no idea how.
Parent Component using React.createClass:
line_items = [
{
id: 1,
unit_amount: "250.00",
...
},
{
...,
unit_amount: "20.00",
...
}
]
//render
<LineItems items={this.props.line_items} />
Child Component using React.createClass:
getInitialState(){
return{
creditedValues: []
}
},
_valueChange(e){
this.setState({creditedValues: this.state.creditedValues.concat([e.target.value])})
},
render(){
var items = this.props.items;
if (items.length !== 0) {
var lineItems = items.map(function(l){
return(
<tr key={l.id}>
<td data-label="Payment">{l.description}</td>
<td className="credit-note-qty" data-label="Issue Date">{l.quantity}</td>
<td className="credit-note-up" data-label="Amount">{l.unit_amount}</td>
<td className="credit-note-amt" data-label="Period">
<input type="number" value={this.state.newValue} onChange={this._valueChange} />
</td>
</tr>
)
}.bind(this));
};
return(<tbody>{lineItems}</tbody>)
}
Before I changed to concat, when I change a value, all values change at the same time. How to prevent this?
Edit:
I have updated the _valueChange with:
_valueChange(i, e){
e.preventDefault();
var obj = this.props.creditNote.line_items;
var num = obj.find(p => i === p.id);
num.unit_amount = e.target.value;
this.setState({creditedValues : num});
}
Input now changes to:
<input type="number" value={this.state.newValue} onChange={this._valueChange.bind(this, l.id)} />
Now I have exactly what I want but there is a but. If the user update another input field, it replace the entire state. True, as the code I have replaces the entire state. If the user update two or more fields, how to keep the new objects?