I'm trying to create a table from a JSON response formulated from a submitted form, therefore the initial render needs to be blank, but this blank state is proving to be an issue.
The issue is complicated further by the fact that the response could have different headers, number of columns, and order.
Parent component
This gets the resultData and passes it to a child component
<ReportsTable title={this.props.title} resultData={this.state.resultData} />
Child component
var ReportsTable = React.createClass({
render: function() {
var resultData = this.props.resultData;
return(
<div>
<h3>{this.props.title}</h3>
<table>
<tr>
//iteration here
</tr>
</table>
</div>
)
}
});
Any attempt at iteration gives me a
Uncaught TypeError: Cannot read property XXX of undefined
The Data received is in this type of format
[Array[1], Array[1]]
0: Array[1]
0: Object
family_name: "Sales"
gross_margin: "0%"
net_profit: "$0.00"
profit_percent: "0%"
quantity_on_hand: 2863
retail: "$9,347.12"
total_cost: "$7,615.96"
total_sold: 49
1: Array[1]
0: Object
family_name: "Service"
gross_margin: "0%"
net_profit: "$0.00"
profit_percent: "0%"
quantity_on_hand: 147.5
retail: "$939.05"
total_cost: "$268.40"
total_sold: 10.8
[UPDATE]
So we modified the response from the server so that I get one less nest in the Array. But now when I try
resultData.map(function(item) { })
and I get an "Uncaught TypeError: undefined is not a function" error as I'm trying to map through the properties of the Object. When I try to map through an Array it works so I don't think it's my syntax.
In the end, my trouble is iterating through the properties of each Object.
This part from the parent works
{resultData.map(function(tableRow, i) {
return (
<TableRow tableRow={tableRow} key={i} />
);
})}
This part in the Child Component does not
var TableRow = React.createClass({
render: function(){
var tableRow = this.props.tableRow;
console.log(tableRow);
return(
<tr key={tableRow}>
{tableRow.map(function(tableItem, i){
<td key={i}>{tableItem}</td>
})}
</tr>
);
}
});
I have had the same problem.
The reason why i got "Uncaught TypeError: undefined is not a function" was because i tried to iterate over the properties of a json object using map which is not possible. The solution for me was to iterate over the keys of the json object using Object.keys(). See below for my solution.
Data:
{
"status": {
"build": {
"a":"b",
"c":"d"
}
}
}
`render: function(){
var buildInfo = this.props.applicationInfo.status.build;
var properties = Object.keys(buildInfo).map((k, idx) => {
return (
<p key={idx}><strong>{k}</strong> - {buildInfo[k]}</p>
);
});
return(
<div>
{properties}
</div>
);
}`
If you have JSON instead of Array and you want to loop in JSX react render function use Object.keys:
<select className="form-control" >
{Object.keys(item.unit).map(unit => {
return <option value="1">1</option>
})}
</select>
So this works
<table className="table table-condensed table-striped">
<thead>
<tr>
{resultTitles.map(function(title){
var textAlignLeft = false;
if(textLeftMatch.test(title)){
textAlignLeft = true;
}
title = title.replace(/_/g, " ");
return <th key={title} className={textAlignLeft ? 'text-left' : ''}>{title}</th>
})}
</tr>
</thead>
<tbody>
{resultData.map(function(tableRow, i) {
return (
<TableRow tableRow={tableRow} key={i} />
);
})}
</tbody>
</table>
var TableRow = React.createClass({
render: function(){
var tableRow = this.props.tableRow;
var rowArray = $.map(tableRow, function(value, index){
return [value];
});
return(
<tr key={tableRow}>
{rowArray.map(function(tableItem, i){
return <td key={i} className={(i === 0) ? 'text-left' : ''}>{tableItem}</td>
})}
</tr>
);
}
});
However, after searching for awhile, I found a better starting point found here http://dynamictyped.github.io/Griddle/quickstart.html
Related
I am trying to render my array of objects inside my table but it shows "Cannot read property 'monthlytarget' of undefined", I am using axios to fetch the result and render inside the table
Axios :
http.get(apiReportsEndpoint+"?empid="+this.props.match.params.id)
.then(response =>{
this.setState({
report:response.data.data.monthlytarget
})
});
Response I receive from API
"data":{
"monthlytarget":[
{
"quarter":1,
"period_start":"2019-04-01",
"monthlytarget":{
"04":{
"targetpm":"213120",
"invoice_values":[
],
"revenuepm":0,
"targetpercentage":0,
"joinees":0
},
"05":{
"targetpm":"213120",
"invoice_values":[
],
"revenuepm":0,
"targetpercentage":0,
"joinees":0
}
}
},
{ quarter":2 ...},
{ quarter":3 ...},
]
}
I want to render values inside "monthlytarget" as rows inside table
<thead>
<tr>
<th>MONTH</th>
<th>TARGET PER MONTH</th>
<th>REVENUE PER MONTH</th>
</tr>
</thead>
<tbody>
{
this.state.report.map((revenuereport) =>{
{Object.keys.map.monthlytarget(premise,index) => (
<tr>
<td>{index}</td>
<td>{premise.targetpm}</td>
<td>{premise.revenuepm}</td>
</tr>
))}
})
}
</tbody>
To create one table out of all the data you could do the following:
this.state.report
.map(({ monthlytarget }) => Object.entries(monthlytarget))
.flat()
.map(([key,value], index) => (
<tr key={index}>
<td>{index}</td>
<td>{value.targetpm}</td>
<td>{value.revenuepm}</td>
</tr>
));
what do you mean by calling Object.keys.map.monthlytarget? if you are trying to loop the array and get JSX, do this:
this.state.report.map((revenuereport) =>
Object.keys(revenuereport.monthlytarget).map((premise, index) => (
<tr>
<td>{index}</td>
<td>{revenuereport.monthlytarget[premise].targetpm}</td>
<td>{revenuereport.monthlytarget[premise].revenuepm}</td>
</tr>
))
);
Do pay attention to indents and brackets, code snippet in the question seems not gonna work at all.
It should be...
this.state.report.map(({ monthlytarget }, i) =>
Object.values(monthlytarget).map({ targetpm, revenuepm }, i) =>
<tr>
<td>{i}</td>
<td>{targetpm}</td>
<td>{revenuepm}</td>
</tr>
))
EDIT: heres the console.log(data) the data is displaying fine, it's just not filtering the data properly..]
!https://imgur.com/a/SsEDAKj!
EDIT 2: this.state.items is an array.
I'm trying to implement a search function, that allows the user to search through the data brought back from the API. I keep getting the following error:
"TypeError: data.filter is not a function"
constructor(){
super();
this.state ={
items: [],
sessions: [],
loading: true,
search: '',
direction: 'asc',
filteredPosts: [],
query: ''
}
this.onSort = this.onSort.bind(this);
this.searchTerm = this.searchTerm.bind(this);
//this.filteredPosts = this.filteredPosts.bind(this);
}
searchTerm =(event) =>{
const query = event.target.value;
this.setState({query}, () => this.filterData());
}
filterData(){
let data = this.state.items;
let src = this.state.query;
data = data.filter(function(data){
return data.indexOf(src) != -1;
});
this.setState({filteredPosts: data});
console.log(this.state.filteredPosts);
}
async getTalks(){
const response = await fetch ('PRIVATE_API');
const data = await response.json();
//console.log(data);
this.setState({items: data});
}
async componentDidMount(){
this.getTalks();
}
render(){
return (
<div className="container-fluid m-0">
<div className="row h-100">
<div className="col-12 ml-0"><h2>Conference Talks</h2>
<p>You can search the talks via title, speaker or tags</p>
<input className="form-control mb-2" id="search" type="text" onChange={this.searchTerm} placeholder="search"></input></div>
<table className="table table-hover table-dark">
<thead>
</thead>
<tbody id ="list">
{this.state.items.map(function(item,index){
return(
<tr key = {index}>
<th data-title="id"scope="row">{item.id}</th>
<td data-title="title" style={{cursor: "pointer"}} title data-original-title={"Click to sort"} data-toggle="title" >{item.title}</td>
<td data-title="description" id="desc">{item.description}</td>
<td data-title ="speaker">{item.speaker}</td>
<td >{item.session}</td>
<td >{item.tags}</td>
<td >{item.time}</td>
<td >{avg}</td>
I can't figure out the right direction to go in when trying to filter through the data that is pulled. What mistakes am I making? ( I have only included relevent code)
Have you run a check to see if your data is there? It's possible the items are not available on first render, causing this problem. Maybe something like
{this.state.items.length > 0 && this.state.items.map(function(item,index){
//rest of your code here
Also, I noticed you are bringing in a loading variable. If this is working you could do
if(loading){ return (<LoadingComponent />}else{ //regular return statement
The issue was I wasn't returning the object attribute.
data = data.filter(function(data){
return **data.title**.toLowerCase().indexOf(src) !== -1;
});
I am getting "Uncaught TypeError: this.state.data.map is not a function" error even I can successfully log data to console from the API.
I have found similar questions but, haven't come up with a good solution to solve this issue yet.
I have read here that, "Objects, {}, in JavaScript does not have the method .map(), it's only for Arrays, []."
However, I can not figure out how to fix this issue, iterate over an object and retrieve data to the React front end too.
Thank you and any help would be greatly appreciated.
import React, { Component } from "react";
import axios from "axios";
export default class GetSubjects extends Component {
constructor(props) {
super(props);
this.getsubjects = this.getsubjects.bind(this);
this.onSearch = this.onSearch.bind(this);
this.state = {
keyword: "",
data: []
};
}
getsubjects(e) {
this.setState({ keyword: e.target.value });
}
onSearch(e) {
e.preventDefault();
const searchsub = {
keyword: this.state.keyword
};
axios
.get(`http://localhost:3000/api/messages/courses/${this.state.keyword}`)
.then(response => {
console.log(response);
this.setState({
data: response.data
});
});
console.log(this.state.keyword);
console.log(this.state.data);
}
componentDidMount() {}
render() {
return (
<div>
<br />
<div>
<label>Course Name</label>{" "}
<input
placeholder="Enter Course Name"
type="text"
value={this.state.keyword}
onChange={this.getsubjects}
name="keyword"
required
/>{" "}
<button className="btn btn-primary" onClick={this.onSearch}>
Get Subjects
</button>
</div>
<br />
<table className="table table-bordered">
<thead>
<tr>
<th scope="col">Course Name</th>
<th scope="col">Subjects</th>
</tr>
</thead>
<tbody>
{this.state.data.map(function(subject) {
return (
<tr>
{" "}
<td key={subject.id}>{subject.name}</td>{" "}
<td key={subject.id}>{subject.subjects}</td>{" "}
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
You've said what you're receiving is:
{
"subjects": ["Computer Architecture", "Basic Networking"],
"_id": "5cf368bfb58f8c35bc19cebc",
"name": "Software Engineering",
"passmark": 75,
"lectureIncharge": "John Smith",
"__v": 0
}
Your setState call sets data to that object, which is not an array.
Your render code expects that to be an array, though. It tries to loop through it and use it to fill in rows in a table. But you're only getting one piece of data: A single course ("Software Engineering") which covers two subjects ("Computer Architecture" and "Basic Networking").
So your render code shouldn't be trying to use that as an array (and so you may not want a table anymore). It should just use data's name and subjects properties directly.
I'll keep your table markup for now, but note that this only produces a single row (because there's only one piece of data). Prior to the return, I'd grab the course:
const course = this.state.data;
then where you're outputting your table:
<tbody>
{course && course.name
? undefined
:
<tr>
<td key={course.id}>{course.name}</td>{" "}
<td key={course.id}>{course.subjects.join(", ")}</td>{" "}
</tr>
}
</tbody>
I've used an explicit join(", ") to join the subjects rather than implicitly via toString, which wouldn't include a space after the comma.
response.data is an object which axios fills when the request is done.
It's true you're initializing data as an array, but when you do:
this.setState({ data: response.data }); You've actually changed it to an object.
Parse the object you get to an array or do something else with the returned data.
Edit: After you response: just do: this.setState({data: response.data.subject});
//yo lo solucione poniendo data.data
//porque (data) solo me devolvía un objeto con varios atributos y data era uno de esos
//entonces le agregue data.data.
getCategorias(){
axios.get("http://localhost:8000/api/categorias")
.then(data => {
this.setState({
data: data.data
});
}).catch((error)=>{
console.error(error)
});
}
componentDidMount(){
this.getCategorias();
}
My program generates few inputs and i try to push data to my state's array's
export default class TransmittersTable extends Component {
constructor() {
super()
this.state = {
axisX: [],
axisY:[],
power: [],
}
}
updateAxisX(e) {
this.setState({
axisX: this.state.axisX.push(e.target.value)
})
}
updateAxisY(e) {
this.setState({
axisY: this.state.axisY.push(e.target.value)
})
}
updateAxisPower(e) {
this.setState({
power: this.state.power.push(e.target.value)
})
}
generateTransmittersItems(){
let transmitters = [];
for(let i = 0; i < this.props.numberOfTransmitters; i++) {
transmitters.push(
<tr>
<td><input id={i} ref="axisX" type="text" onChange={this.updateAxisX.bind(this)}/></td>
<td><input id={i} ref="axisY" type="text" onChange={this.updateAxisY.bind(this)}/></td>
<td><input id={i} ref="power" type="text" onChange={this.updateAxisPower.bind(this)}/></td>
</tr>
);
}
return transmitters
}
componentWillMound(){}
render() {
return (
<table>
<thead>
<tr>
<th>Axis X</th>
<th>Axis Y</th>
<th>Power</th>
</tr>
</thead>
<tbody>
{this.generateTransmittersItems()}
</tbody>
</table>
)
}
}
In first row of inputs evrything is okay but if i try to push another value form another row of input's to the same state array (ex. axisX) my console send me this error "this.state.axisX.push is not a function".
What i did wrong and what i have to do to push more values to the same array from input using the same function?
I think the problem isn't related to the react state issue.
When you used the "push" methods, it won't return an array but return the length of the array, and that is the reason why when you use "push" method in second time will get the error "this.state.axisX.push is not a function".
So, if you need to change your state, you can just use "concat" method like this to get a new array as return:
this.setState({
axisX: this.state.axisX.concat([e.target.value])
})
var a = ["Hi", "There"];
var b = a.push("Oh");
console.log(b); // get 3, not an array
console.log(a); // ["Hi", "There", "Oh"]
I am building a React.js application where I want to allow users to input styles in a text area which will affect the style of another element.
First, I have a row component which looks like this:
function Row(props) {
return (
<tr>
<td width="50%">
<textarea
style={{}}
value={props.style}
onChange={props.onStyleChange}>
</textarea>
</td>
<td><div className="">Row {props.id+1}</div></td>
</tr>
)
};
I am iterating through a list of rowData to populate my rows, which can be found here:
{this.state.rowData.map(function(row, index) {
return (
<Row
id={row.id}
style={row.style}
onStyleChange={function(e) {this.onStyleChange(e, row.id)}.bind(this)}/>
);
}.bind(this))}
Then in my onStyleChange function I have:
onStyleChange: function(e, id) {
this.state.rowData[id].style = e.target.value;
this.setState(this.state);
}
So as a user enters data into the the textarea, it adds to the i'th element in my rowData array. Assuming its the 0th row and the user enters "Hello" into the text area, then rowData[0].style = "Hello".
However, I would like to be able to do something like this: style={{props.style}} inside my Row component. But because it is currently a string it does not work. I have also tried style={JSON.parse(props.style)} which throws an error every time I add a new row because props.style='{}'. The error reads Uncaught SyntaxError: Unexpected token f in JSON at position 1
Always grateful for any help. There's got to be a much easier way to do this. Thank you.
Two steps to convert inline-style toobject-style` as restricted by React :
Parse the string to be a JSON object.
Convert the keys of this object to camel case (z-index becomes zIndex.. so on)
Congrats! i wrote the algorithm , check below :
const example1= "position:absolute;h-index:9001;"
const example2 = "-ms-transform: rotate(20deg);-webkit-transform: rotate(20deg);";
// 2ⁿᵈ step logic
const camelize = (string) => string.replace(/-([a-z])/gi,(s, group) => group.toUpperCase());
// 1ˢᵗ step logic which calls the 2ⁿᵈ step logic
const style2object = (style) => style.split(';').filter(s => s.length)
.reduce((a, b) => {
const keyValue = b.split(':');
a[camelize(keyValue[0])] = keyValue[1] ;
return a;
} ,{});
console.log("Example 1 : ", example1, '\n',
style2object(example1)
)
console.log("Example 2 : ", example2, '\n',
style2object(example2)
)
If it is helpful the style attribute needs an object like {"color": "blue"}
I made a demo with your code the only thing that escapes me is how to update with the onChange event.
function Row(props) {
const styleValue = JSON.stringify(props.style);
return (
<tr>
<td width="50%">
<textarea
style={props.style}
defaultValue={styleValue}
onChange={props.onStyleChange}/>
</td>
<td><div className="">Row {props.id+1}</div></td>
</tr>
)
};
class App extends React.Component {
state = {
rowData: [{
id: 1,
style: {
color: 'blue'
}
}, {
id: 2,
style: {
color: 'red',
backgroundColor:'#000'
}
}]
};
onStyleChange(e, id) {
const rows = this.state.rowData;
const row = rows.find(row => row.id === id);
const index = rows.indexOf(row);
rows[index]['style'] = JSON.parse(e.target.value);
this.setState({
rowData: rows
});
}
render() {
return (
<table>
<tbody>
{
this.state.rowData.map(function(row, index) {
return (
<Row
id={row.id}
key={index}
style={row.style}
onStyleChange={function(e) {this.onStyleChange(e, row.id)}.bind(this)}/>
);
}.bind(this))
}
</tbody>
</table>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
http://codepen.io/GGarciaSeco/pen/vgVEGX?editors=0010
You can take a look to the React documentation in the next link
https://facebook.github.io/react/docs/dom-elements.html#style