reactjs table render - how to handle empty rows rendering for tbody - reactjs

I am very new to ReactJS and playing around to learn its tricks of the trade. I am trying to create a simple table with thead and tbody.
my code looks as below:
/**
* creates a HTML table to show requirements
**/
var ReqTable = React.createClass({
render: function() {
var rows = [];
rows = this.props.data.map(function(row) {
return ( <ReqRow row={row} />);
});
return(
<table className="reqTable table table-bordered table-striped">
<thead>
<tr>
<th>REQ #</th>
<th>Short Desc</th>
<th>Long Description</th>
</tr>
</thead>
<tbody> {rows} </tbody>
</table>
);
}
});
I am using ajax call to get the rows and render this table. This works fine overall, but gives a warning in the browser console, indicating that you cannot add a span inside a tbody.
I am sure I am not adding a span. But what I believe is that when there are no rows (when render is first time called), it seems reactJS is trying to render a span inside the tbody.
So, should I put an if check here to avoid rendering a span? Finally what is the correct way to render a empty set of rows in tbody?

Your issue is that you have space characters as children of your tbody element, before and after your rows array:
<tbody> {rows} </tbody>
Compiles to:
React.createElement("tbody", null, " ", rows, " ");
Because of DOM limitations, when a DOM component has more than one child, React wraps all its text children within <span> elements. So the above JSX is equivalent to:
<tbody><span> </span>{rows}<span> </span></tbody>
To fix this, just remove the extraneous characters:
<tbody>{rows}</tbody>

Related

Table rendering issue from API responce in ReactJs

I am new to react and facing some issues with a table display.
When I am trying to render data using State entire string value gets rendered, instead of an actual table.
A small snippet of data displayed in the browser:
But when I copy-paste the data from the screen to the render() method, the table gets displayed properly.
The current render method-
render() {
this.setValueForState();
return (
<div>
<table>
<thead>
<tr>
{this.state.Header}
</tr>
</thead>
<tbody>
{this.state.Detail}
</tbody>
</table>
</div>
);
}
I get the following error in the console. I have made sure that there are no BLANK spaces in the data.
index.js:1 Warning: validateDOMNesting(...): Text nodes cannot appear as a child of <tr>.
Warning: validateDOMNesting(...): Text nodes cannot appear as a child of <tbody>.
I looked up this error online for the above error and removed all Blank spaces from the result string.
Not sure what am I doing wrong.
this.state.Header and this.state.Detail are strings, which are getting rendered as text nodes.
As the error message states, you can not put text nodes inside <tr> or <tbody> tags as you have done.
HTML tables need the text nodes to be inside <td> tags.
See below for valid table markup.
<div>
<table>
<thead>
<tr>
<td>{this.state.Header}</td>
</tr>
</thead>
<tbody>
<tr>
<td>{this.state.Detail}</td>
</tr>
</tbody>
</table>
</div>
Please remove some whitespace. you can try with below
<tr key={id}>{this.state.Header}</tr>;

React.js, handling onMouseOver event

being a noob in React I'm facing a problem: the component returns a simple nested table, so each cell of the 'big-table-id' contains another small table 'small-table-id'.
Thing is that every time the mouseover event occurs I'm always getting the 'small-cell-*' as target.id, even if the event handler is referenced in the parent (big) table. Is there any way to get the parent table kinda 'non-transparent' so that I could receive 'big-table-cell-1' or 'small-table-id'?
(using Rails with 'react-rails' gem)
var Tables = React.createClass({
handleMouseOver: function(e){
console.log(e.target.id)
},
render: function(){
return (
<table id='big-table-id' onMouseOver={this.handleMouseOver}>
<tr>
<td id='big-table-cell-1'>
<table id='small-table-id'>
<tr>
<td id='small-cell-1>
text 1
</td>
<td id='small-cell-2'>
text 2
</td>
</tr>
</table>
</td>
</tr>
</table>
)
}
});
The DOM lets you select an elements child and parent nodes with methods like firstChild and parentElement. You should look into those.
Edit: also not sure if this would work but you could try wrapping the big table in a div and setting the callback there and seeing what it references.

Cannot find react component

It throw me Error: Cannot find react component myTable.. i have that react..
var myTable = React.createClass({
render:function () {
return(
<div>
<table className="table scroll2" >
<thead>
<tr>
<td>Start Date</td>
<td>End Date</td>
<td>Hours</td>
<td></td>
</tr>
</thead>
{
this.props.list.map(function (item,i) {
return(
<tr>
<td>{item.startDate}</td>
<td>{item.endDate}</td>
<td>{item.workInHours}</td>
</tr>
);
})
}
</table>
</div>
);
}
});
angular.module('app').value('myTable',myTable);
and i call that with this :
<react-component name="myTable"></react-component>
Declare your component like this (notice the Pascal Casing):
var MyTable = React.createClass({ ... })
Use your component like this instead:
<MyTable />
As Justin has pointed out correctly, React class names must start with an upper-case letter, otherwise it will be interpreted as html tag. Thus, a lot of people follow Pascal Case.
From the official docs:
To render a React Component, just create a local variable that starts with an upper-case letter
JSX in Depth: HTML Tags vs. React Components
#Mario Tacke You're right with the exception that it HAS to be pascal cased if its a React element, otherwise it will be interpreted as a HTML tag element and not a react one.
More about it here https://gist.github.com/sebmarkbage/f1f4ba40816e7d7848ad

React.js component apparently renders, but no change in what's displayed

This is my first venture into React territory, so I'm sure my problem is a simple newbie one. As an exercise I'm trying to convert an existing working web application to use React. I have a ProductTable which displays a list of products. It has a 'selectedProducts' field as part of its state, which gets updated whenever the product selection changes. My assumption was that changing this value (state) should cause a re-rendering with any new selection.
On first display, the component displays successfully and correctly shows the list of products. However, when a change is made to the selection, what is displayed on screen does not change. Yet from console logging and use of React tools in Chrome, I can see that the 'selectedProducts' field state has indeed changed, and in fact a console log entry in the render method shows that render for the component is in fact being called, with the new selection. But it's not changing what's displayed on screen - the table continues to show the old selection of products - and I really can't figure that out.
Any suggestions as to where to look? I appreciate it might be clearer with some code here but it's slightly difficult to present it in a concise way given that it's a conversion of an existing web application, so I hope my description above is sufficient to suggest something to the experts here.
EDIT: Code of component added here. As you will see, notification is done by means of pub/sub. I've stuck with the global 'productSelection' object for now, but that would be refactored in due course. At the moment the idea is that some other code makes changes to that global variable and then notifies the ProductTable component via pub/sub. As I mentioned, the notification is working fine, the 'selectedProducts' variable is clearly being changed and render is being run - but no visible change is occurring.
var ProductTable = React.createClass({
getInitialState: function () {
return {
selectedProducts: [],
};
},
componentWillMount: function(){
this.pubsubToken = Arbiter.subscribe('products/selection', function() {
// update my selection when there is a message
console.log("Setting selection to "+productSelection.length);
this.setState({ selectedProducts: productSelection });
}.bind(this));
},
componentWillUnmount:function(){
Arbiter.unsubscribe(this.pubsubToken);
},
render: function () {
var rows = this.state.selectedProducts.map(function (product, i) {
return <ProductRow key={product.code} product={product}/>;
});
console.log("Running render for ProductTable with "+rows.length+" rows");
return (
<div>
<label htmlFor="products" id="productsLabel">Available products (click or double-click to select):</label>
<table id="products" className="st-container">
<thead>
<tr>
<td className="st-head" style={{padding:"0px 18px 0px 0px"}}>
<table cellSpacing="0" cellPadding="0" border="0" className="st-head-table" style={{width: "100%"}}>
<thead>
<tr>
<th className="rem" colSpan="0"></th>
<th className="code" colSpan="0">Code</th>
<th className="name" colSpan="0">Name</th>
<th className="pack" colSpan="0">Pack</th>
<th className="size" colSpan="0">Size</th>
<th className="attribs" colSpan="0"></th>
<th className="price" colSpan="0">Price</th>
<th className="unit" colSpan="0">Unit</th>
<th className="rrp" colSpan="0">RRP</th>
<th className="vat" colSpan="0">VAT</th>
</tr>
</thead>
</table>
</td>
</tr>
</thead>
<tbody>
<tr>
<td className="st-body" style={{padding:"0px"}}>
<div className="st-body-scroll" style={{overflowY: "scroll", maxHeight: "250px"}}>
<table cellSpacing="0" cellPadding="0" border="0" className="st-body-table" style={{width: "100%"}}>
<tbody id="productRows">
${rows}
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
);
}
});
Ok I see, you need to use componentDidMount instead of componentWillMount because, as stated in the doc : If you call setState within this method, render() will see the updated state and will be executed only once despite the state change.
https://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount
Use componentDidMount and everything should be fine.
Edit:
you don't need ${rows} but just {rows}. Also I would suggest you check your new data, are they really different form the initial one ?

ng-show tr element inside ng-repeat

I'm still pretty new to angular and am having trouble laying out the logic for my problem
Basically, I am using ng-repeat to generate a table filled with rows, and upon clicking any given row, to have a hidden row appear beneath the clicked on, so that I can load it up with data
My code looks as follows
<table class="table table-hover">
<tbody ng-repeat="order in resp.Orders" >
<tr ng-click='selectOrder(order);'>
<td>{{order.OrderId}}</td>
<td>{{order.Client}}</td>
</tr>
<tr ng-show('order.IsSelectedOrder')>
<td>Selected Order</td>
</tr>
</tbody>
</table>
As the code stands, the list generates with both rows visible at all times. At this point, that is the main problem
My selectOrder function looks as follows
$scope.selectOrder = function (order) {
order.IsCurrentOrder = true;
}
The syntax is incorrect and I think the property you set in the selectOrder function is incorect. It should be:
<tr ng-show="order.IsSelectedOrder">
$scope.selectOrder = function (order) {
order.IsSelectedOrder = true;
}
Docs for ng-show

Resources