I've got the following component in ReactJS
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData);
var categories = this.props.groupsData.objects.map(function(obj){
return (<li>{obj.display_name}</li>);
});
return (<div className="MainMenu">
<ul className="nav nav-pills">{categories}</ul>
</div>);
}
});
Now, I wish to add className='active' to the <li> element if its the first in the map. How would I achieve this?
Use JS in your expression
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData);
var categories = this.props.groupsData.objects.map(function(obj, index) {
// You can have a JavaScript expression in your expression
return ( <li className={index == 0 ? 'active' : ''}> <a href="#">{
obj.display_name
}</a></li> );
});
return ( <div className="MainMenu" >
<ul className="nav nav-pills" >{
categories
}</ul>
</div> );
}
});
Related
Iam new to ReactJS and I am working on a filterable gallery but now Iam looking at an example of thinking in ReactJS and I saw that they are building different classes for each component. I didn't do that but now Iam trying to do this, First my code looked like this: `
var SearchBar = React.createClass({
getInitialState() {
return { text:'', array: this.props.array};
},
handleChange(event) {
var array = this.filterList(event.target.value);
this.setState({ text: event.target.value, array: array });
return this.state.text;
},
render() {
var arrayComponents = this.state.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
return <div>
<h1>Hello, {this.props.name}</h1>
<p>{this.state.text}</p>
<input type="text" onChange={this.handleChange} />
<ul>
{arrayComponents}
</ul>
</div>;
},
filterList (filterText) {
var updatedList = this.props.array,
filterTextLength = filterText.length;
return updatedList.filter(function(item){
var splitName = item.name.toLowerCase().slice(0, filterTextLength);
var lowerCaseFilterText = filterText.toLowerCase();
return splitName === lowerCaseFilterText;
});
}
});
Now I want to create an other Class of ImageList which has to include the var arrayComponents but if i do this:
var ImageList = React.createClass({
render() {
var arrayComponents = this.props.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
<ul>
{arrayComponents}
</ul>
}
})
and than in the render function add <ImageList array={array}/> instead of <ul>{arrayComponent}</ul> it throws me the error that Cannot read property 'map' of undefined How do I pass the state of array into that ImageList class.
Here is a codepen: LINK
I've made the following changes in your code: http://codepen.io/PiotrBerebecki/pen/zKRAGZ
1) Pass the state of array into that ImageList class
<ImageList array={this.state.array} />
2) Add a return statement in the render method of ImageList
// Add return
return (
<ul>
{arrayComponents}
</ul>
);
3) Add key attribute to the li tag when using map method:
var arrayComponents = this.props.array.map(function(photo, index) {
// -------------------------------
// Add index to the li tag
// ----------------vvvvvvvvvvv
return <li key={index} className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
React Docs: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
You need to pass the current state of the array. So your component declaration should look like this:
<ImageList array={this.state.array}/>
I glance three problems here.
First,
var ImageList = React.createClass({
render() {
var arrayComponents = this.props.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
<ul>
{arrayComponents}
</ul>
}
})
you should say:
return (<ul>
{arrayComponents}
</ul>)
Second, when you use dynamically generated codes, you should add a key prop to it:
var arrayComponents = this.props.array.map(function(photo) {
return <li key={SOME_KEY} className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
Third, in your codepen when you say :
<ImageList array={array}/>
you are referencing the global variable array (you declare it on the top of your code), do you mean:
<ImageList array={this.state.array}/>
Below is my sample code .....
<ul>
{this.state.showAllUser == false
? someArray.slice(0, 3).map(function(user, index) {
return (
<li key={index}> {user.name}<li>
)
: someArray.map(function(user, index) {
return (
<li key={index}> {user.name}<li>
)
}
</ul>
If this.state.showAllUser is false, i will only show three of array or show all of them if true.
My question is how to make this code more clean , can I make a function or variable and use it in refer function?
You could use the Array method instead, like so:
<ul>
{someArray.filter(function(el, index) {
if (!this.state.showAllUser) {
// Print the first 3 elements
return (index < 3)
} else {
// Print all
return true
}
})
.map(function(user, index) {
return (<li key={index}> {user.name}</li>)
})
}
</ul>
In this way it is very clear where you control which elements are going to be shown and which are not.
And more you write only once the virtual DOM part.
<ul>
{
(this.state.showAllUser == false ? someArray.slice(0, 3) : someArray).map((user, index) => <li key={index}> {user.name}<li>);
}
</ul>
Simple React State Example. The parent component is App which starts out just showing a button, when the button is clicked it should render AllRecipes (this works and I am able to manage state of AllRecipes). Inside AllRecipes is a button which needs to trigger a state change to then further render ingredients (this button does nothing when clicked, it needs to toggle the state of ingredients). I think this is a really good small example of how to manage state but I am missing something..
var App = React.createClass({
getInitialState: function(){
return {showIngredients: false, showRecipes: false};
},
toggleRecipes: function(){
this.setState({showRecipes: !this.state.showRecipes})
},
toggleIngredients: function(){
this.setState({showRecipes: !this.state.showRecipes})
},
render: function() {
var recipes = this.state.showRecipes ? <Recipes/> : null;
return (
<div>
<h1> Recipe App </h1>
<button onClick={this.toggleRecipes}> Show Recipes </button>
{recipes}
</div>
);
}
});
var Recipes = React.createClass({
render: function() {
var ingredients = this.props.showIngredients ? <Ingredients/> : null;
return (
<div>
<h1> list of recipes </h1>
<ul>
<li> Chicken Soup </li>
<li> Chicken Wings </li>
<button onClick={this.props.toggleIngredients}> Show Recipes </button>
{ingredients}
</ul>
</div>
);
}
});
var Ingredients = React.createClass({
render: function() {
return (
<div>
<h1> List of Ingredients </h1>
<ul>
<li> Salt </li>
<li> Pepper </li>
</ul>
</div>
);
}
});
React.render(<App/>, document.body);
It looks like you aren't passing toggleIngredients to Recipes. Try changing
var recipes = this.state.showRecipes ? <Recipes/> : null;
to
var recipes = this.state.showRecipes ? <Recipes toggleIngredients={this.toggleIngredients} /> : null;
How do i get this :
<li>
<div class='myClass1'>myData1</div>
<div class='myClass2'>myData2</div>
<div class='myClass3'>myData3</div>
<div class='myClass4'>myData4</div>
</li>
from this code
var data1 = {"Columns":[{"Title":"Title1","HTMLClass":"g1_Title"},{"Title":"Title2","HTMLClass":"g2_Title"},{"Title":"Title3","HTMLClass":"g3_Title"}],"Rows":[{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]}]};
var GridRow = React.createClass({
render: function() {
var data = [], columns;
// puts all the data in to a better structure (ideally the props would have this structure or this manipulation would be done on onReceiveProps)
if(this.props.columns){
for(var ii = 0; ii < this.props.columns.length; ii++){
data.push({
class: this.props.columns[i].HTMLClass,
contents: this.props.Cell[i]
})
}
}
// Its best to map JSX elements and not store them in arrays
columns = data.map(function(col) {
return <div className= + {col.class}> {col.contents} </div>;
});
return (
<div>
<li>
{columns}
</li>
</div>
);
}
});
var GridHead = React.createClass({
render: function() {
if(this.props.data){
var cell = this.props.data.Title;
var htmlClass = this.props.data.HTMLClass;
}
return (
<div className={htmlClass}>{cell}</div>
);
}
});
var GridList = React.createClass({
render: function() {
if(this.props.data){
var header = this.props.data.Columns.map(function (columns) {
return (
<GridHead data={columns} />
);
});
var row = this.props.data.Rows.map(function (row, i) {
return (
<GridRow columns={data1.Columns} cells={row.Cells} key={i} />
);
});
}
return (
<ul>
<li>{header}</li>
{row}
</ul>
);
}
});
var GridBox = React.createClass({
render: function(){
return (
<GridList data={data1} />
);
}
});
The output right now is this
In file "~/Scripts/Grid.jsx": Parse Error: Line 26: XJS value should
be either an expression or a quoted XJS text (at line 26 column 35)
Line: 52 Column:3
As your question initially asked was to do with just the GridRow component and nothing else I have not touched any other component.
Your main problem was you were assigning className = + //something in your GridRow component which isn't the correct way to assign. There were other errors like missing div tags.
Better GridRow
When the component mounts a columndata variable is created and is populated with formatted data using formatData();.
I do not recommend you do data formatting in this component (although it is doable). You should either format your data at a top level component and pass down formatted data or accept data in the correct structure.
My GridRow component to this:
var GridRow = React.createClass({
componentWillMount: function() {
this.columndata = [];
this.formatData();
},
formatData: function() { // Formats prop data into something manageable
if (this.props.columns && this.props.cells) {
for(var ii = 0; ii < this.props.columns.length; ii++){
this.columndata.push({
class: this.props.columns[ii].HTMLClass,
contents: this.props.cells[ii]
})
}
this.forceUpdate(); // Forces a rerender
}
},
componentDidUpdate: function(prevProps, prevState) {
// If this component receives the props late
if (!prevProps.cells && !prevProps.columns) {
this.formatData();
}
},
render: function() {
var columns;
// Its best to map JSX elements and not store them in arrays
columns = this.columndata.map(function(col) {
return <div className={col.class}> {col.contents} </div>;
});
return (
<div>
<li>
{columns}
</li>
</div>
);
}
});
I think it's important to note that you should avoid storing JSX elements in arrays.
I think you were basically on the money, except you were missing classname and div tags.
My angularJS application displays the items of an object. If a single item has a certain ID I want to display a message. At the moment it does not work, what is wrong?
js fiddle
HTML
<div data-ng-controller="myCtrl">
<ul >
<li data-ng-repeat="item in values">
Item with id:<code>#{{item.id}}</code>
<code ng-hide="special(item.id)"> -> This id is special</code>
</li>
</ul>
</div>
NG
var app = angular.module('m', []);
app.controller('myCtrl', function ($scope) {
$scope.values = [{
id: 1
}, {
id: 2
.....
}];
$scope.filter = [4,5,6];
$scope.filterIds = function (ids) {
return function (item) {
var filter = $scope.filter;
return filter.indexOf(item.id) !== -1;
}
}
$scope.special = function (id) {
return function (id) {
var filter = $scope.filter;
return filter.indexOf(id) !== -1;
}
}
});
$scope.special() returns a function inside of itself, so the return value is a function and not a boolean value. Replace it with this:
$scope.special = function (id) {
var filter = $scope.filter;
return filter.indexOf(id) !== -1;
}
and you'll see that it works.
please see here http://jsfiddle.net/1rhvuyL1/
$scope.special = function (id) {
if ($scope.filter.indexOf(id) >=0)
{return true;}
}
HTML:
<div>
<div data-ng-controller="myCtrl">
<ul>
<li data-ng-repeat="item in values">Item with id:<code>#{{item.id}}</code> <code ng-show="special(item.id)"> -> This id is special</code>
</li>
</ul>
</div>
</div>
You dont need to create the special function inside the JS.
Try:
<div data-ng-controller="myCtrl">
<ul >
<li data-ng-repeat="item in values">
Item with id:<code>#{{item.id}}</code>
<code ng-hide="item.id == '1'"> -> This id is special</code>
</li>
</ul>
</div>
The above will hide the "This id is special" when id = 1