Grouping variable number of elements with React / JSX - reactjs

I have a situation where I have a variable number of objects in an array, and I need to create groups of three. I know that to iterate over an array I can do something like this (Fiddle - https://jsfiddle.net/o5pbttjq/):
render: function() {
var random = Array.apply(null, Array((Math.floor(Math.random() * (15 - 1) + 1) + 1))).map(Number.prototype.valueOf,0).map(function(d,i) {return i});
return (
<div>
{random.map(function(d) {
return <p>{d}</p>
})}
</div>
);
}
I can also group them like so, but it's quite ugly (Fiddle - https://jsfiddle.net/Lxqm3tzo/):
render: function() {
var random = Array.apply(null, Array((Math.floor(Math.random() * (15 - 1) + 1) + 1))).map(Number.prototype.valueOf,0).map(function(d,i) {return i});
var content = "<div>";
random.forEach(function(d, i) {
if (i % 3 === 0 && i !== 0) {
content += "</div><div>"
}
content += "<p>" + d + "</p>"
})
content += "</div>"
return (
<div dangerouslySetInnerHTML={{__html: content}}></div>
);
}
How can I create groups of a variable number of objects with JSX?

The following will work without having to set the innerHTML. See the fiddle here: https://jsfiddle.net/kuvvd513/8/
var Hello = React.createClass({
render: function() {
var random = Array.apply(null, Array((Math.floor(Math.random() * (15 - 1) + 1) + 1))).map(Number.prototype.valueOf,0).map(function(d,i) {return i});
var allGroups = [];
var group = [];
random.forEach(function(d, i) {
if (i % 3 === 0 && i !== 0) {
allGroups.push(<div> {group} </div>);
group = [];
}
group.push(<p> {d} </p>);
}.bind(this));
allGroups.push(<div> {group} </div>);
return (
<div>
{allGroups}
</div>
);
}
});
React.render(<Hello name="World" />, document.getElementById('container'));

Related

How to use conditional to generate element on the page

for (var k = 0; k < 10; k++) {
if (k % 2 === 0) {
weatherText = <div className="in_break">
}
weatherText += <div className="eachD" key={k}>
<div>
{
countIt === 0 ? (currDate.getHours() > 12 ? "Tonight" : "Today") : dayOfWeek[weekDay]
}
</div>
<div>
{
getDate
}
</div>
<div>
{
<ReturnIcon />
}
</div>
</div>
if (k % 2 === 0) {
weatherText += </div>
}
}
What I am looking to do is group all the eachD by two inside the `in_break' div
But I keep getting:
Parsing error: Unexpected token 'weatherText = </div>'
This is the layout:
in_break
eachD
eachD
in_break
eachD
eachD
in_break
eachD
eachD
...
Please help me resolve my issue
UPDATED
I hope this find it's way to your demand:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
renderInBreak = () => {
const items = this.setWeatherTextItems();
const inBreakItems = [];
let breakBlock = [];
let newBreak = false;
items.forEach((textItem, index) => { //1
if(!newBreak) {
breakBlock.push(textItem);
if(index + 1 === items.length){
inBreakItems.push(breakBlock);
}
} else {
inBreakItems.push(breakBlock);
breakBlock = [];
breakBlock.push(textItem);
//without this condition check, the last element will be left out of an odd array length
if(index + 1 === items.length) {
inBreakItems.push(breakBlock)
}
}
if(index % 2) newBreak = true; //false
else newBreak = false; //false
});
return inBreakItems.map(twoTextWeatherItems => (
<div className="in_break">
{twoTextWeatherItems}
</div>
))
}
render(){
<div>
{this.renderInBreak()}
</div>
}
OLD
React is supposed to handle things differently, maybe this will work:
Define a method in your component that will set your items:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
in your render method, or where you are willing to render these items:
render(){
<div className="in_break">{this.setWeatherTextItems()}</div>
}
Read more about how to render things in a loop.
You can add the conditions you want inside the for loop, or where it makes sense to you.
Not sure if the logic would work in a react environment but as far as I can see from your plain code when you are going to add the 'in_break' div aren't you just assigning the whole whetherText again instead of joining text to it?
Shouldn't this:
if (k % 2 === 0) {
weatherText = </div>
}
be written like this?
if (k % 2 === 0) {
weatherText += </div>
}
Edit following the typo correction:
I tried to run your code on codepen to have a quicker and easier understanding on how to find a solution.
I created an helper function with your code then I returned
<div className="Container" dangerouslySetInnerHTML={{__html: weatherText}}></div>
This enables you to have the result you are looking for. Only the even elements have the 'in_break' class.
Hope this helped and let me know if this is not correct.
Codepen: https://codepen.io/dpgian/pen/EBzRmX

How to dynamically use object property as width in AngularJS (in ng-repeat)?

I cannot get the object's property to be read in ng-style(shape.radius || shape.length). I can't even get 1 to work at the moment, but would like to have an or statement included. Similar to my ng-class.
There is a button to generate shapes, and the shapes were created with a random size. Here is my code:
html:
<div ng-controller='ShapeController as sc'>
<div>
<p><input type="submit" value="Generate Random Shapes" ng-click="sc.generateShapes()"/></p>
<div ng-repeat="shape in sc.shapes">
<div class="shape" ng-class="{circle: shape.radius, square: shape.length}" ng-style="{'width': shape.length}"></div>
</div>
</div>
</div>
script:
var app = angular.module('app', []);
app.controller('ShapeController', function(){
var vm = this;
vm.shapes = [];
vm.randomShapes = [];
vm.width = 30;
function createCircle(radius) {
let circle = new Circle(radius);
vm.shapes.push(circle);
} // end createCircle
function createSquare(length) {
let square = new Square(length);
vm.shapes.push(square);
} // end createSquare
vm.generateShapes = function() {
let times = 50
for (let i = 0; i < times; i++) {
createCircle(getRandomNumber());
}
for (let i = 0; i < times; i++) {
createSquare(getRandomNumber());
}
sort(vm.shapes);
console.log(vm.shapes);
}; // end generateShapes
}); // end controller
function sort(arr) {
arr.sort(function(a,b){
return b.getArea() - a.getArea();
});
} // end sort function
function getRandomNumber() {
return Math.random() * (100-1) + 1;
}
width should be either in px(some unit like em, pt, etc) or %
ng-style="{'width': shape.length + 'px'}"

View variable does not get injected in the $scope

The view:
<div class="numbers">
{{calc.amount || 0 }}
</div>
<div class="keypad">
<div class="number-slot"><button ng-click="numberPressed(1)" class="number">1</button></div>
</div>
The controller:
app.controller('pay-amount', function ($scope, $state, $cordovaBarcodeScanner, ionicMaterialInk) {
$scope.numberPressed = function (number) {
console.log(number + " pressed!");
$scope.calc = {
amount: "" + $scope.calc.amount + number
}
};
ionicMaterialInk.displayEffect();
});
There are no errors till numberPressed(1) is called:
"ionic.bundle.js:26794 TypeError: Cannot read property 'amount' of
undefined".
Upon inspecting $scope, I can't find the "calc" object I defined in the view. What am I missing here?
You need to declare the $scope variable before using it,
$scope.calc = {amount:0};
DEMO
Try this,
$scope.numberPressed = function (number) {
console.log(number + " pressed!");
$scope.calc = {amount:''};
$scope.calc = {
amount: "" + $scope.calc.amount + number
}
console.log($scope.calc);
};
Your calc was never set on the $scope so it tries to find amount on undefined
You could make 2 simple changes
$scope.calc = {};
$scope.numberPressed = function (number) {
console.log(number + " pressed!");
$scope.calc.amount = ($scope.calc.amount || 0) + number
}
or
$scope.calc = { amount: 0 };
$scope.numberPressed = function (number) {
console.log(number + " pressed!");
$scope.calc.amount = $scope.calc.amount + number
}
Try this,
$scope.calc = { amount: 0 };
$scope.numberPressed = function (number) {
console.log(number + " pressed!");
$scope.calc.amount = $scope.calc.amount + number
}

cannot read property of -1 in ReactJS multidimensional grid

I'm at my witsend with this problem. I'm building Conway's game of life and right now I'm trying to find neighbors(cells that are on top, to the bottom, and on the sides of the target cell). When I console log my function (basically to find the neighbors), I get values for the bottom and middle rows and all columns but for the top row I get "Cannot read property '-1' of undefined". Sometimes it will return "cannot read property of 'r' of defined" as well. I'm confused because I get return values when i console log both row (r) and columns (c). I'm also curious as to why I don't see this same error message when dealing with my columns. The messages on developer tools are very cryptic. I've spent quite a bit of time trying to figure this out but I'm truly stuck. Any help would be much appreciated.
I tried to do a JSbins: https://jsbin.com/bapazem/edit?html,css,js,output but its not liking the construction of my react components. You can see these areas clearly on my codepen: http://codepen.io/tbraden30/pen/AXgVNQ. Just click the play button and dev tools
Thanks in advance for your help
function randomGrid() {
var gridHeight = 20,
gridWidth = 20,
gridContainer = [];
for (var r = 0; r < gridWidth; r++) {
var row = [];
for (var c = 0; c < gridHeight; c++) {
row.push({
alive: Math.random() < 0.2,
neighbors: 0,
r: r,
c: c,
coords: [r, c]
});
}
gridContainer.push(row)
}
return gridContainer;
}
function isWithinBounds(cell) {
if (cell.r >= 0 && cell.r < 20 && cell.c >= 0 && cell.c < 20) {
return true
}
}
var grid = randomGrid();
var Board = React.createClass({
getInitialState: function() {
return {
cellLocation: randomGrid()
};
},
updateCells: function(r, c) {
var grid = this.state.cellLocation;
for (var r = 0; r < 20; r++) {
for (var c = 0; c < 20; c++) {
var cell = grid[r][c];
//console.log('cell', cell)
cell.neighbors = 0;
var isAlive = cell.alive;
var bottomLeft = grid[r + 1][c - 1],
bottomMiddle = grid[r + 1][c],
bottomRight = grid[r + 1][c + 1],
middleLeft = grid[r][c - 1],
middleRight = grid[r][c + 1],
topLeft = grid[r - 1][c - 1], **//problematic**
topMiddle = grid[r - 1][c], **//problematic**
topRight = grid[r - 1][c + 1];**//problematic**
/* if (isWithinBounds(bottomLeft) && isAlive) {
cell.neighbors++;
}
if (isWithinBounds(bottomMiddle) && isAlive) {
cell.neighbors++;
}
if (isWithinBounds(bottomRight) && isAlive) {
cell.neighbors++;
}
if (isWithinBounds(middleLeft) && isAlive) {
cell.neighbors++;
}
if (isWithinBounds(middleRight) && isAlive) {
cell.neighbors++;
}
if (isWithinBounds(topLeft) && isAlive) {
cell.neighbors++;
}
if (isWithinBounds(topMiddle) && isAlive) {
cell.neighbors++;
}
if (isWithinBounds(topRight) && isAlive) {
cell.neighbors++;
}*/
console.log('TR', topRight)
}
}
return cell;
},
render: function() {
return (
<div className='container grid_body'>
<CellMatrix grid={this.state.cellLocation}/>
<button type='button' onClick={this.updateCells} className='play'>Play</button>
</div>
)
}
}, this)
var CellMatrix = React.createClass({ //cells conglomerated into a grid
render: function() {
var fillCells = [],
grid = this.props.grid,
grid = grid.reduce((a, b) => a.concat(b)) //flattens the array.
grid.map(function(cellObject, index) {
fillCells.push(<Cell alive={cellObject.alive} r={cellObject.r} c={cellObject.c} coords={cellObject.coords} index={index} neighbors={this.updateCells}/>)
// console.log('fillcells', fillCells)
}, this)
return (
<div>
{fillCells}
</div>
)
}
})
var Cell = React.createClass({ //individual cell
render: function() {
return (
<td className={this.props.alive}
index={this.props.index}
coords={this.props.coords}
r={this.props.r}
c={this.props.neighbors}
neighbors={this.props.neighbors}
>
{this.props.children}
</td>
)
}
})
ReactDOM.render(<Board/>, document.getElementById('content'))
it sounds like an off by 1 error. when you generate the grid, you go from index 0..19, yet in the updateCells function you have a loop that sets c = 0, then subtracting 1 from it, meaning youre trying to access grid[-1][-1] when r and/or c is 0 (probably not what youre trying to do). then the fatal error likely occurs when r is 19 and c is 0, and you add one to r and subtract 1 from c. you end up accessing grid[20][-1] and since grid[20] is undefined, you get the error Cannot read property '-1' of undefined

Using for loop render some Html elements in React render function

I am trying to render some html using for loop. Every thing work fine but the html prints like a string inside that UL element I dont know what I did wrong pls help me with this. I am new to this React Js thing.
My Script
const Pagination = React.createClass({
getInitialState: function () {
return {
"page": {
"size": 100,
"limit": 30,
"offset": 0,
"number": 1
}
};
},
render: function () {
var html = [];
var i = 0;
var className = '';
var noOfPages = 0;
var number = 1;
var page = this.state.page;
if (page) {
noOfPages = parseInt((page.size / page.limit), 10);
for (i; i < noOfPages; i++) {
className = (className === number) ? 'selected' : '';
html.push('<li key=' + {i} + ' className=""><button className="' + {className} + '">' + {i} + '</button></li>');
}
}
return (
<div className="pagination-sec-wrapper">
<form className="pagination-inner-sec-wrapper" id="pagination-form">
<ul className="">
{html}
</ul>
</form>
</div>
);
}
});
That's because you are pushing string values into your HTML array.
html.push('<span key=' + {i} + ' className=""><button className="' + {className} + '">' + {i} + '</button></span>');
You need to push React components instead.
html.push(<span key={i} className="">
<button className={className}>{i}</button>
</span>);
It's important to remember that even though JSX looks like HTML strings, it's really just syntactic sugar for regular Javascript function calls.
The above JSX is just the same as the following JS.
html.push(React.createElement("span", { key: i, className: "" },
React.createElement("button", { className: className }, i)));

Resources