Create table using iterative statements in "react" rendering - javascript

I try to create a table with two arrays in a state(week_today, week_count) and a repeating sentence when rendering.
I don't know how to use map() function . Please advise.
my code
render() {
const {week_today, week_count} = this.state; // this.state in my array put
return (
<div>
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">today</th>
<th scope="col">count</th>
</tr>
</thead>
<tbody>
//<== here my array table
</tbody>
</table>
</div>
)
}
use my arrays
week_today = [ 01,02,03,04,05]
week_count = [ 1,2,3,4,5]

Try below. Just a sample code
constructor(props) {
super(props);
const week_today = ['01', '02', '03', '04', '05'];
const week_count = [1, 2, 3, 4, 5];
this.state = {
week_today: week_today,
week_count: week_count
};
}
render() {
const {week_today, week_count} = this.state; // suggest you to use destructuring your variables:
return (
<div>
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">today</th>
<th scope="col">count</th>
</tr>
</thead>
<tbody>
{week_today.map((today, index) =>
<tr key={index}>
<td>{today}</td>
<td>{week_count[index]}</td>
</tr>
)}
</tbody>
</table>
</div>
)
}

Slightly changed the data structure (add this to your state):
week: [
{ today: '01', count: 1 },
{ today: '02', count: 2 },
{ today: '03', count: 3 },
{ today: '04', count: 4 },
{ today: '05', count: 5 }
]
And add this to your render:
<tbody>
{this.state.week.map( element => {
return (
<tr>
<td>
{element.today}
</td>
<td>
{element.count}
</td>
</tr>
)
})
}
</tbody>

Related

Incrementing a state by 1 via a button, in React/JavaScript [duplicate]

I need to build a table in order to organize some data. I'm using the "onclick" function in order to call a separate function, which is supposed to increment a state variable up by one. My Chrome Devtools isn't giving me any errors but also isn't updating the stock variable. I'm not sure how to get the state to update and display. Here's my source code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
cars: [
{
manufacturer: "Toyota",
model: "Rav4",
year: 2008,
stock: 3,
price: 8500
},
{
manufacturer: "Toyota",
model: "Camry",
year: 2009,
stock: 2,
price: 6500
},
{
manufacturer: "Toyota",
model: "Tacoma",
year: 2016,
stock: 1,
price: 22000
},
{
manufacturer: "BMW",
model: "i3",
year: 2012,
stock: 5,
price: 12000
},
]
};
this.renderCar = this.renderRow.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(() => {
return {stock: this.stock + 1}
})
}
renderRow(car, index) {
return (
<tr key={index}>
<td key={car.manufacturer}>{car.manufacturer}</td>
<td key={car.model}>{car.model}</td>
<td key={car.year}>{car.year}</td>
<td key={car.stock}>{car.stock}</td>
<td key={car.price}>${car.price}.00</td>
<td key={index}><input type="button" onClick={car.handleClick} value="Increment" /></td>
</tr>
)
}
render() {
return (
<div>
<table>
<thead>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Year</th>
<th>Stock</th>
<th>Price</th>
<th>Option</th>
</tr>
</thead>
<tbody>
{this.state.cars.map(this.renderRow)}
</tbody>
</table>
</div>
);
};
}
ReactDOM.render(<App />, document.getElementById("app"))
I'd make a separate component for the row, so that you can easily update that component to increment the stock value in state:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
cars: [
{
manufacturer: "Toyota",
model: "Rav4",
year: 2008,
stock: 3,
price: 8500
},
{
manufacturer: "Toyota",
model: "Camry",
year: 2009,
stock: 2,
price: 6500
},
{
manufacturer: "Toyota",
model: "Tacoma",
year: 2016,
stock: 1,
price: 22000
},
{
manufacturer: "BMW",
model: "i3",
year: 2012,
stock: 5,
price: 12000
},
]
};
}
render() {
return (
<div>
<table>
<thead>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Year</th>
<th>Stock</th>
<th>Price</th>
<th>Option</th>
</tr>
</thead>
<tbody>
{this.state.cars.map(
(car, i) => <Row car={car} key={i} />
)}
</tbody>
</table>
</div>
);
};
}
const Row = ({ car }) => {
const [stock, setStock] = React.useState(car.stock);
return (
<tr>
<td>{car.manufacturer}</td>
<td>{car.model}</td>
<td>{car.year}</td>
<td>{stock}</td>
<td>${car.price}.00</td>
<td><input type="button" onClick={() => setStock(stock + 1)} value="Increment" /></td>
</tr>
);
}
ReactDOM.render(<App />, document.getElementById("app"))
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id='app'></div>
You could put it all in one component if you had to, but it'd be a bit cumbersome. While rendering, you'd have to keep track of the render index of a row and pass that along to the click handler, then immutably update the stock property in the state array at that index. A separate component is easier.
handleClick(e){
const index = Number(e.currentTarget.value);
this.setState(this.state.cars.map(car, i)=> {
return i === index ? {...car, stock: car.stock + 1}: car
})
}
renderRow(){
....
<input type="button" onClick={this.handleClick} value={index} />
...
}

Keep getting TypeError: Cannot read property 'map' of undefined when I click on checkbox, dont know where the problem is

So I keep getting this error when I click on checkbox and the desired outcome would be once the checkbox is clicked the property of active should change to opposite. Even If I remove the activeHandler function once i click on checkbox I get the same error but now for the initial mapping of products in tbody
const ProductList = props => {
const [products, setProducts] = useState(
[
{
id: 1,
name: 'Product 1',
ean: 242355,
type: 'Food',
weight: 24,
color: 'blue',
active: true,
quantity: 2,
price: 25
},
{
id: 2,
name: 'Product 2',
ean: 57434,
type: 'Food',
weight: 48,
color: 'red',
active: false,
quantity: 5,
price: 12
}
]
);
const activeHandler = productId => {
setProducts(prevState => {
const updatedProducts = prevState.products.map(prod => {
if (prod.id === productId) {
prod.active = !prod.active
}
return prod
})
return {
products: updatedProducts
}
})
}
return (
<div>
<table className="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">EAN</th>
<th scope="col">Type</th>
<th scope="col">Weight</th>
<th scope="col">Color</th>
<th scope="col">Active</th>
<th></th>
</tr>
</thead>
<tbody>
{products.map(product => (
<tr key={product.id}>
<td>{product.name}</td>
<td>{product.ean}</td>
<td>{product.type}</td>
<td>{product.weight}</td>
<td>{product.color}</td>
<td>
<input type="checkbox" checked={product.active} onChange={() => activeHandler(product.id)} />
</td>
<td>
<button className="btn btn-secondary mr-1" disabled={product.active}>VIEW</button>
<button className="btn btn-primary mr-1" disabled={product.active}>EDIT</button>
<button className="btn btn-danger" disabled={product.active}>DELETE</button>
</td>
</tr>
))
}
</tbody>
</table>
</div>
)
}
Your prevState is the actual array in this case, so you should map over it and also return it as the new state and not an object with a products key:
setProducts(prevState => {
const updatedProducts = prevState.map(prod => {
if (prod.id === productId) {
prod.active = !prod.active
}
return prod
})
return updatedProducts
})

Javascript put data on <template> and append it on tbody after axios request

I have here a table in which a person's data will be displayed
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Username</th>
<th scope="col">Birthdate</th>
<th scope="col">Age</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<!-- existing data could optionally be included here -->
</tbody>
</table>
this template is will be use on putting the data and append it on the tbody
<template id="persons">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</template>
this is the JavaScript code that I have
let oCRUD = {
init: function() {
this.setDOMElements();
this.getPersons();
},
// Setting DOM Elements
setDOMElements: function() {
this.oTemplate = document.querySelector('#persons'); //persons row
this.oTbody = document.querySelector("tbody");
this.oClone = document.importNode(oCRUD.oTemplate.content, true);
this.oTd = oCRUD.oClone.querySelectorAll("td");
},
getPersons: function() {
axios.get('selectAll.php')
.then(function (response) {
response.data.forEach((element,index) => {
oCRUD.oTd[0].textContent = element.name;
oCRUD.oTd[1].textContent = element.username;
oCRUD.oTd[2].textContent = element.birthdate;
oCRUD.oTd[3].textContent = element.age;
oCRUD.oTd[4].textContent = element.email;
oCRUD.oTbody.appendChild(oCRUD.oClone);
});
})
.catch(function (error) {
console.log(error);
});
}
}
// call the init function
oCRUD.init();
How can I use the template put the data there after the successful response of axios and append it on the tbody. This is my first time using DOM templating I have no idea how to start it.
This is the successful response after axios get request
[
{
id: "1",
name: "john",
username: "john doe",
birthdate: "1999-05-21",
age: "20",
email: "test#gmail.com",
},
{
id: "2",
name: "sally",
username: "sally mcsalad",
birthdate: "1999-03-27",
age: "20",
email: "try#gmail.com",
},
]
EDIT: I SUCCESSFULLY SHOW THE DATA HOWEVER I ONLY GOT THE SECOND SET OF DATA (sally mcsalad) NOT THE WHOLE DATA
Your main problem is you only clone the node, and select the tds once. This counts as a single object, which will just update the existing elements on each iteration. You need to refresh the clone and the selected tds on each iteration
var data = [
{
id: "1",
name: "john",
username: "john doe",
birthdate: "1999-05-21",
age: "20",
email: "test#gmail.com",
},
{
id: "2",
name: "sally",
username: "sally mcsalad",
birthdate: "1999-03-27",
age: "20",
email: "try#gmail.com",
},
];
let oCRUD = {
init: function() {
this.setDOMElements();
this.getPersons();
},
// Setting DOM Elements
setDOMElements: function() {
this.oTemplate = document.querySelector('#persons'); //persons row
this.oTbody = document.querySelector("tbody");
this.oClone = document.importNode(oCRUD.oTemplate.content, true);
this.oTd = oCRUD.oClone.querySelectorAll("td");
},
refreshClone: function() {
this.oClone = document.importNode(oCRUD.oTemplate.content, true);
this.oTd = oCRUD.oClone.querySelectorAll("td");
},
getPersons: function() {
/*axios.get('selectAll.php')
.then(function (response) {*/
data.forEach((element,index) => {
oCRUD.refreshClone();
oCRUD.oTd[0].textContent = element.name;
oCRUD.oTd[1].textContent = element.username;
oCRUD.oTd[2].textContent = element.birthdate;
oCRUD.oTd[3].textContent = element.age;
oCRUD.oTd[4].textContent = element.email;
oCRUD.oTbody.appendChild(oCRUD.oClone);
});
/*})
.catch(function (error) {
console.log(error);
});*/
}
}
// call the init function
oCRUD.init();
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Username</th>
<th scope="col">Birthdate</th>
<th scope="col">Age</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<!-- existing data could optionally be included here -->
</tbody>
</table>
<template id="persons">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</template>
add the function generateTable to your code and call it after success your request
function generateTable(persons){
let oTbody = document.querySelector("tbody");
if(!persons) return;
persons.forEach( person=>{
let tr =
`<tr id=${person.id}>
<td>${person.name}</td>
<td>${person.username}</td>
<td>${person.birthday}</td>
<td>${person.age}</td>
<td>${person.email}</td>
</tr>`
oTbody.insertAdjacentHTML('beforeend', tr);
})
}
let persons = [
{
id: "1",
name: "john",
username: "john doe",
birthdate: "1999-05-21",
age: "20",
email: "test#gmail.com",
},
{
id: "2",
name: "sally",
username: "sally mcsalad",
birthdate: "1999-03-27",
age: "20",
email: "try#gmail.com",
},
]
generateTable(persons)
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Username</th>
<th scope="col">Birthdate</th>
<th scope="col">Age</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<!-- existing data could optionally be included here -->
</tbody>
</table>
Use template function in general, such as lodash.template or jQuery.tmpl, you can also implement a simple template function.
Step1: define a template function to transit each data object to html string.
# use ES2015 string template feature
function transitData(data){
return '<tr>' +
`<td class="record">${data.name}</td>` +
`<td>${data.email}</td>` +
'</tr>';
}
Step2: get Server response and render your data collection(such as array).
axios.get('selectAll.php').then(response => {
let html = response.data.map(transitData).join("\n");
oTbody.insertAdjacentHTML('beforeend', html);
});

bind multidimensional array with vuejs

I am building a dynamic table on my front end side, and at the end i need to know what was inserted on each cell of my table since it is editable, so i did this on my html:
<table class="table table-responsive">
<tbody>
<tr v-for="(row,idx1) in tableRows" :class="{headerRowDefault: checkHeader(idx1)}">
<td class="table-success" v-for="(col,idx2) in tableCols"><input v-model="items[idx1][idx2]" type="text" class="borderTbl" value="HEY"/></td>
</tr>
</tbody>
</table>
as you guys can see. i set inside the input a v-model with items[idx1][idx2] so i can pass the value to that line and columns, it is not working like this, i don't know how to set it.
This is my javascript:
export default {
name: 'app',
data () {
return {
table: {
rows: 1,
cols: 1,
key: 'Table',
tableStyle: 1,
caption: '',
colx: []
},
hasHeader: true,
hasCaption: true,
insert: 1,
idx2: 1,
items: []
}
},
computed: {
tableStyles () {
return this.$store.getters.getTableStyles
},
tableRows () {
return parseInt(this.table.rows)
},
tableCols () {
return parseInt(this.table.cols)
}
expected items array:
items:[
["john","Micheal"]
["john","Micheal"]
["john","Micheal"]
["john","Micheal"]
]
So, I think you're not pointing your models correctly.
Template:
<tr v-for="(row, idx1) in items">
<td class="table-success" v-for="(col, idx2) in row">
<input v-model="items[idx1][idx2]" type="text" />
</td>
</tr>
Script:
data () {
return {
items:[
["john","Micheal"],
["john","Micheal"],
["john","Micheal"],
["john","Micheal"]
];
};
}
Here's a working fiddle of it

Display data in a table by grouping them horizontally

I have some data that has the following format:
[name:'Name1', speed:'Val1', color:'Val2']
[name:'Name2', speed:'Val4', color:'Val5']
[name:'Name3', speed:'Val6', color:'Val7']
That I want to display in a table like this:
|Name1|Name2|Name3|
______|_____|______
speed |Val1 |Val4 |Val6
color |Val2 |Val5 |Val7
What I tried to do is group my data like this in the controller:
$scope.data = {
speeds: [{
...
},{
...
},{
...
}],
colors: [{
...
},{
...
},{
...
}],
};
But I am not sure what to put inside the empty areas, because all values there represent the values of the 'val1' variable for all Names (Accounts), and my tests until now keep failing.
You can imagine this as some sort of a comparisons matrix, that is used in order to see the all the values of the same variable across different accounts.
How can I represent the data in my model in order for me to successfully display them in a table as explained?
Edit
My difficulty lies in the fact that you create a table by going from row to row, so my html looks something like this:
<table md-data-table class="md-primary" md-progress="deferred">
<thead>
<tr>
<th ng-repeat="header in headers">
{{header.value}}
</th>
</tr>
</thead>
<tbody>
<tr md-auto-select ng-repeat="field in data">
<td ng-repeat="var in field">{{var.value}}</td>
</tr>
</tbody>
</table>
So as you can see I have a loop for each row, and a loop for each value of each row. This would be easier if I wanted to display the data horizontally, but I want the data vertically. So if we where talking about cars, we would have the car models as headers, and their respective characteristics(speed, color, etc) in each row.
If this is your basic structure:
var cols = [{name:'Name1', val1:'Val1', val2:'Val2'},
{name:'Name2', val1:'Val4', val2:'Val5'},
{name:'Name3', val1:'Val6', val2:'Val7'}];
This code
$scope.table = cols.reduce(function(rows, col) {
rows.headers.push({ value: col.name });
rows.data[0].push({ value: col.speed });
rows.data[1].push({ value: col.color });
return rows;
}, {headers:[], data:[[], []]});
will give you this structure for $scope.table:
$scope.table = {
headers : [{
value : "Name1"
}, {
value : "Name2"
}, {
value : "Name3"
}
],
data : [
[{
value : 'val1'
}, {
value : 'val4'
}, {
value : 'val6'
}
],
[{
value : 'val2'
}, {
value : 'val5'
}, {
value : 'val17'
}
]
]
};
<table md-data-table class="md-primary" md-progress="deferred">
<thead>
<tr>
<th ng-repeat="header in table.headers">
{{header.value}}
</th>
</tr>
</thead>
<tbody>
<tr md-auto-select ng-repeat="field in table.data">
<td ng-repeat="var in field">{{var.value}}</td>
</tr>
</tbody>
</table>
You could try this:
HTML
<table ng-app="myTable" ng-controller="myTableCtrl">
<thead>
<tr>
<th ng-repeat="car in cars">{{car.name}}</th>
</tr>
</thead>
<tbody>
<tr>
<td ng-repeat="car in cars">{{car.speed}}</td>
</tr>
<tr>
<td ng-repeat="car in cars">{{car.color}}</td>
</tr>
</tbody>
</table>
JS
angular.module("myTable",[])
.controller("myTableCtrl", function($scope) {
$scope.cars = [
{
name:'Name1',
speed:'Val1',
color:'Val2'
},
{
name:'Name2',
speed:'Val4',
color:'Val5'
},
{
name:'Name3',
speed:'Val6',
color:'Val7'
}
]
});
https://jsfiddle.net/ABr/ms91jezr/

Categories