Reputation: 1215
Please forgive my ignorance. I am just getting into using react and I am sure I am missing something really simple here. So depending on the amount of data I get back from loadData I need to display the data in 1 2 or 3 columns. I am using ul and li. I calculate based on screen size how many can fit in a column. I tried having them all in one ul but I need a header row at the top, so I really need up to 3 uls with the first li having the header info. How can I loop and create the number of uls that I need? I tried putting a loop around the return but I can't do that. Any help is greatly appreciated.
var appointmentBoardSection = React.createClass({
displayName: "appointmentBoardSection",
getInitialState: function() {
return {
key: null,
results: [],
parameters: this.props.parameters,
type: "",
};
},
shouldComponentUpdate: function(nextProps, nextState) {
console.info("this.state.key: " + this.state.key);
console.info("nextState.key: " + nextState.key);
if (this.state.key !== nextState.key) {
console.info("NO MATCH!!!!");
//this.forceUpdate();
return true;
}
return false;
},
componentDidMount: function() {
console.info("MOUNTED!!!");
var _this = this;
_this.loadData();
setInterval(function() {
_this.loadData();
}, 10000);
},
loadData: function() {
console.info("LOADING DATA....");
var _this = this;
$.when(_api.callPromise('apiRouter.ashx', 'getAppointmentsBoard', JSON.stringify(masterPage.reporting.parameters), this.props.type)).then(function(data) {
var totalData = data.args[0];
if (totalData.length <= 0) {
$("div#" + _this.props.type).hide();
} else {
$("#no_data").hide();
$("div#" + _this.props.type).show();
}
_this.setState({
results: totalData,
key: Math.random()
});
masterPage.hidePleaseWait(true);
});
},
render: function() {
var contHeight = $(window).height();
var offsetTop = $("div.appt_bd_container").offset().top;
var realEstate = contHeight - 400;
var perColumn = Math.round(realEstate / 70);
console.info("height: " + $(window).height());
console.info("offsetTop: " + offsetTop);
console.info("perColumn: " + perColumn);
var maxICanShow = perColumn * 3;
var limitResults = false;
var colCount = 3;
var colDef = "";
var colWidth = 100;
var numberOfColsNeeded = Math.round(this.state.results.length / perColumn);
if (numberOfColsNeeded > 3) {
colCount = 3;
} else {
colCount = numberOfColsNeeded;
}
switch (colCount) {
case 2:
colWidth = 50;
break;
case 3:
colWidth = 33.333;
break;
default:
colDef = "";
colWidth = 100;
}
if (this.state.results.length > maxICanShow) {
var i = 1;
console.info("this.state.results.length: " + this.state.results.length);
console.info("maxICanShow: " + maxICanShow);
while (i < this.state.results.length - (maxICanShow - 1)) {
this.state.results.pop();
i++;
}
}
var curColData = [];
var overallCount = this.state.results.length;
var testArr = [];
for (var i = 0; i < colCount; i++) {
testArr.push({});
}
if (this.state.results.length > perColumn) {
for (var i = 0; i < perColumn; i++) {
if (overallCount > 0) {
curColData.push(this.state.results.shift());
overallCount--;
}
}
}
var myTest = [];
return ( <
ul className = "appt_bd" >
<
li className = "appt_bd_header"
style = {
{
width: colWidth + '%'
}
} >
<
span className = "appt_bd_header_main" > {
this.props.title
} < /span> <
span className = "appt_bd_header_sub" > Appts Set < /span> <
span className = "appt_bd_header_sub" > Appts Show < /span> <
/li> {
curColData.map(function(row) {
return <AppointmentBoardRow colWidth = {
colWidth
}
key = {
row.userId
}
row = {
row
}
/>;
})
} <
/ul>
);
}
});
var AppointmentBoardRow = React.createClass({
render: function() {
var row = this.props.row;
return ( <
li className = "appt_bd_row"
style = {
{
width: this.props.colWidth + '%'
}
}
key = {
row.userId
} >
<
span className = "appt_bd_desc" >
<
span className = "appt_bd_rank" > {
row.rank
} < /span> <
span className = "appt_bd_avatar_container" > < div className = {
row.className
} > < span className = "initials" > {
row.initials
} < /span></div > < /span> <
span className = "appt_bd_user" >
<
span className = "appt_bd_description_main" > {
row.userName
} < /span> <
span className = "appt_bd_description_sub" > {
row.role
} < /span> <
/span> <
/span> <
span className = "appt_bd_data" >
<
span className = "appt_bd_data_main" > {
row.apptsSetCount
} < /span> <
span className = "appt_bd_data_sub" > /{row.apptsSetGoal}</span >
<
/span> <
span className = "appt_bd_data" >
<
span className = "appt_bd_data_main" > {
row.apptsShowCount
} < /span> <
span className = "appt_bd_data_sub" > /{row.apptsShowGoal}</span >
<
/span> <
/li>
);
}
});
****EDITED in response to Josh...one issue I am having now is nothing is displaying though I can verify uls is correct. Here is my nee render:
render: function () {
var contHeight = $(window).height();
var offsetTop = $("div.appt_bd_container").offset().top;
var realEstate = contHeight - 400;
var perColumn = Math.round(realEstate / 70);
var maxICanShow = perColumn * 3;
var colWidth = 100;
var uls = _.chunk(_.take(this.state.results, maxICanShow), perColumn);
let li = "";
return (
<div>
{uls.map((lis) => {
<ul className="appt_bd">
<li className="appt_bd_header" style={{ width: colWidth + '%' }}>
<span className="appt_bd_header_main">{this.props.title}</span>
<span className="appt_bd_header_sub">Appts Set</span>
<span className="appt_bd_header_sub">Appts Show</span>
</li>
lis.map(li => {<li className="appt_bd_row" style={{ width: this.props.colWidth + '%' }} key={li.userId}>
<span className="appt_bd_desc">
<span className="appt_bd_rank">{li.rank}</span>
<span className="appt_bd_avatar_container"><div className={li.className}><span className="initials">{li.initials}</span></div></span>
<span className="appt_bd_user">
<span className="appt_bd_description_main">{li.userName}</span>
<span className="appt_bd_description_sub">{li.role}</span>
</span>
</span>
<span className="appt_bd_data">
<span className="appt_bd_data_main">{li.apptsSetCount}</span>
<span className="appt_bd_data_sub">/{li.apptsSetGoal}</span>
</span>
<span className="appt_bd_data">
<span className="appt_bd_data_main">{li.apptsShowCount}</span>
<span className="appt_bd_data_sub">/{li.apptsShowGoal}</span>
</span>
</li>})
</ul>
})}
</div>
);
}
Upvotes: 1
Views: 81
Reputation: 313
By using lodash you could simplify your algorithm that builds the layout. See Below for beginning of render
render: function() {
var contHeight = $(window).height();
var offsetTop = $("div.appt_bd_container").offset().top;
var realEstate = contHeight - 400;
var perColumn = Math.round(realEstate / 70);
var maxICanShow = perColumn * 3;
var uls = _.chunk(_.take(this.state.results, maxICanShow), perColumn);
switch (uls.length) {
case 2:
colWidth = 50;
break;
case 3:
colWidth = 33.333;
break;
default:
colDef = "";
colWidth = 100;
}
after this you don't need any of the other code you've written until
return {...
but you will need to map your template to uls
rather than curColData
. It will look something like this (oversimplified):
uls.map((lis) => {
<ul>
<li> my header </li>
lis.map(li => <li></li>)
</ul>
})
It's worth noting that my solution uses double iteration in the return
view which is to be avoided where possible -- especially in potentially large sets; but since your set won't be (given the size of the container) AND you want to dynamically build nested elements it seems the right pattern for the requirement.
now uls
will be an array of arrays that will map your data to your proposed view structure. Specifically, your ul
s will be represented by each array in uls
; then, each item in nested array will represent an li
.
Take uls[0]
as your first ul
and uls[0][0]
will be your first li
of your first ul
.
_.take
documentation can be found here
_.chunk
documentation can be found here
Hopefully, having your data transformed into a similar structure as your desired view will help move you forward.
I'll also say that there's a number of other issues with the code. For example, React folks will likely tell you mixing jQuery in is a no-no.
UPDATE 9/15/17
I thought about it some more and realized there was a more verbose way without double iteration. You want no more than three columns, we can use that to map out three separate ul
elements all containing their chunk of the total items to be displayed. If that chunk happens to not exist (there are fewer items than max this.state.results.length < maxICanShow
), it won't be a problem because of handy dandy lodash. See code below:
class ListPane extends React.Component {
render () {
var contHeight = 900;//$(window).height();
var offsetTop = 50; //$("div.appt_bd_container").offset().top;
var realEstate = contHeight - 400;
var perColumn = Math.round(realEstate / 70);
var maxICanShow = perColumn * 3;
var uls = "abcedefhijklmnopqrstuvwxyz".split("");
var chunks = _.chunk(_.take(uls, maxICanShow), perColumn);
var colWidth = 0;
var colA = _.map(_.nth(chunks, 0), item => <li>{item}</li>)
var colB = _.map(_.nth(chunks, 1), item => <li>{item}</li>)
var colC = _.map(_.nth(chunks, 2), item => <li>{item}</li>)
const listItems = chunks.map((lis) =>
<ul>
<li> {lis} </li>
</ul>
);
switch (chunks.length) {
case 2:
colWidth = 50;
break;
case 3:
colWidth = 33.333;
break;
default:
colWidth = 100;
}
return (
<div>
<ul>
{colA}
</ul>
<ul>
{colB}
</ul>
<ul>
{colC}
</ul>
</div>
)
}
}
Notice I made use of _.nth
which protects the code from a potential null
or unresolveable reference.
Also, check out this codepen
There are other improvements which could be made.
var colC = _.map(_.nth(chunks, 2), item => <li>{item}</li>)
,
it would be cleaner to create another class, perhaps ListItem
in the li
s stead.
So,
var colC = _.map(_.nth(chunks, 2), item => <ListItem data={item} />)
'abcedfghijkl'.split('')
you'll get two columns with the second set of li
positioned at the bottom. I'm not super sure why but didn't feel like it took away from the demonstration.Final Note on React, and why I don't/won't use it. It's license isn't truly open and many people have concerns about it. If you're just getting into React, it may be a good time to choose something else. See this reddit
Upvotes: 2