How to add conditional statements in reactJS map function - reactjs

Am trying to render different tags based JSON keys, but it keeps throwing syntax error...
adding only one of the two conditions works though !
How can I do it right ?
<ul className="features">
{
property.details.features.map((idx, feature) =>
feature.hasOwnProperty(area) ?
<li>
<span className="icon-frame"/>
<div className="english">{feature[area]}m2 </div>
</li> : null
feature.hasOwnProperty(bedRoom) ? <li>
<span className="fa fa-bed"/>
<div className="english">{feature[bedRoom]}</div>
</li> :null
)
}
</ul>

The problem is you have an arrow function without braces in the body of the function, which means the expression right after the arrow will be what that function returns. But you have two expressions in a row there (the two ternary operators), so it won't know what to return, hence the syntax error. You can try this:
<ul className="features">
{
property.details.features.map((idx, feature) =>
feature.hasOwnProperty(area) ?
(
<li>
<span className="icon-frame"/>
<div className="english">{feature[area]}m2 </div>
</li>
)
: feature.hasOwnProperty(bedRoom) ?
(
<li>
<span className="fa fa-bed"/>
<div className="english">{feature[bedRoom]}</div>
</li>
)
: null
)
}
</ul>
So, if feature has the attribute area, then render the first, else, if it has the attribute bedrom, then render the second, else, return null. I tried to make it as readable as possible but it's never easy with long ternary operators.
You can always just wrap the body of the arrow function and do it with if, else and return statements.

Related

Why we use (null) while creating dropdown in react js?

if we have not put null then it gives parsing error.
render() {
return (
<div className="dropdown" style = {{background:"Red",width:"2000px"}} >
<div className="button" onClick={this.showDropdownMenu}> User Info</div>
{this.state.displayMenu ? (
<ul>
<li><a className="active" href="#Orders">Orders</a></li>
<li>Payment Details</li>
<li>Your Address</li>
<li>Profile</li>
<li>Activity</li>
<li>Setting</li>
<li>Log Out</li>
</ul>
):
(
null
)}
</div>
);
}
If you don't want to add null don't use ternary, you can do this:
render() {
return (
<div className="dropdown" style = {{background:"Red",width:"2000px"}} >
<div className="button" onClick={this.showDropdownMenu}> User Info</div>
{ this.state.displayMenu && (
<ul>
<li><a className="active" href="#Orders">Orders</a></li>
<li>Payment Details</li>
<li>Your Address</li>
<li>Profile</li>
<li>Activity</li>
<li>Setting</li>
<li>Log Out</li>
</ul>
)}
</div>
);
}
You should always return a value from an expression. Even if the value it's null. To avoid ternary operators when you have a boolean you could render like this
return condition && <JSX />
For the case that this.state.displayMenu is false, you return null to preventing a component from rendering. In your case it's explicitly set to null.
React docs: https://reactjs.org/docs/conditional-rendering.html#preventing-component-from-rendering
In rare cases you might want a component to hide itself even though it was rendered by another component. To do this return null instead of its render output.

"Use expression as" in ng-repeat

Building a table I am deep within nested ng-repeats.
Depending on a scope variable I have to display/use a different field from my objects.
Is there a shorter way to adress the decision/field call in a way that could look like 'from now on use expression as myVar'?
Currently my code looks very ugly and basically always repeating a pattern like direction==='sources' ? connection.target.X : connection.source.X
As I am already pretty deep I cannot simply rearrange my array in Controller first.
This is just a small part of it:
<strong>{{direction==='sources' ? connection.target.name : connection.source.name}}</strong>
<br>
<a ng-click="goToEntityUsingReport(direction==='sources' ? connection.target.id : connection.source.id)">
<i class="fa fa-plus-square-o"></i>
</a>
<a ng-show="hasFurtherConnections(direction==='sources' ? connection.target.id : connection.source.id)" ng-click="setEntityFocus(direction==='sources' ? connection.target.id : connection.source.id)">
<i class="fa fa-chevron-circle-right"></i>
</a>
{{getEntityTotal(direction==='sources' ? connection.target.id : connection.source.id) | acCurrency}} total)
You can assign this expression on the return statement of a function in your controller in order to look cleaner.
e.g.
$scope.getValueBasedOnProperty(connection, prop) {
if($scope.direction === 'sources') {
return connection.target[prop];
}
else {
return connection.source[prop]
}
}
Then in the view:
<strong>{{getValueBasedOnProperty(connection, 'name')}}</strong>
<br>
<a ng-click="goToEntityUsingReport(getValueBasedOnProperty(connection, 'id')">
<i class="fa fa-plus-square-o"></i>
</a>
<!-- And goes on ... -->

Expression in ng-if of AngularJS

Consider the following snippet
ng-if not working
<div class="row">
<div class="col-md-8">
<ul>
<li ng-repeat="bigL in bigLs">
<span ng-if="isObj(bigL)">{{bigL.note}}</span>
<ul ng-if="bigL instanceof Array">
<li ng-repeat="bigLl in bigL">
{{bigLl}}
</li>
</ul>
</li>
</ul>
</div>
</div>
ng-if working
<div class="row">
<div class="col-md-8">
<ul>
<li ng-repeat="bigL in bigLs">
<span ng-if="isObj(bigL)">{{bigL.note}}</span>
<ul ng-if="isArr(bigL)">
<li ng-repeat="bigLl in bigL">
{{bigLl}}
</li>
</ul>
</li>
</ul>
</div>
</div>
Controller
$scope.isArr = function(bigL){
return bigL instanceof Array;
};
I use ng-if to determine whether a nested ul is required to create (by determinate different data type inside the array bigLs), I come to a situation that ng-if cannot evaluate bigL instanceof Array, I then move this snippet inside a function, with the same context, the ng-of works properly, but still cannot understand why it is a need to wrap the expression inside a function instead of running it directly inside the ng-if.
Appreciate for any clarification, thanks!
I'm not exactly sure of the problem, but there are several things that have bad smells in your code:
Don't use 'instanceof Array', ever. It won't work in an angular expression.
Instead, use angular.isArray(). This will only work in javascript by adding a method to your scope.
So, you would want to do something like this:
Controller:
...
$scope.hasChildren = function(bigL1) {
return angular.isArray(bigL1);
}
Template:
...
<ul ng-if="hasChildren(bigL)">
...
As a bonus, it becomes much easier to unit test this code.

Append to existing JSX instead of using if-else conditional logic

In my react classes, I often find myself having to use conditional logic to decide what to render. The problem with such an approach is that it leads to a lot of redundant markup. Here is an example:
if(this.props.quotes) {
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
<i className="fa fa-quote-left"></i>
<i className="fa fa-quote-right"></i>
</div>
);
}
else {
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
</div>
);
}
The only difference between the two HTML components is that one has two extra font-awesome elements. Ideally, you would want to use some base markup, and append content to it based on the result of the conditional.
I tried the following approach where I put the HTML content into an array and pushed the extra HTML elements in if the condition this.props.quotes was met:
var cardContent = [
<div className="item-1">{this.props.header}</div>,
<div className="item-2">Add content...</div>
];
if(this.props.quotes) {
cardContent.push(<i className="fa fa-quote-left"></i>);
cardContent.push(<i className="fa fa-quote-right"></i>);
}
return (
<div className="card">
{cardContent}
</div>
);
This introduces a new problem, mainly that React complains about missing key props in the array:
Warning: Each child in an array or iterator should have a unique "key" prop.
In this context, it doesn't make sense to give the elements keys since 3/4 of the content is static (the only non-static element is {this.props.header}).
Is there a better way of appending to existing JSX than the method I outlined above? I don't want to suppress all unique key prop warnings since it is still valid in the case of mapping. Is it better to just accept the redundant HTML approach?
When you render an Array of JSX elements, each one must have a key property on them. As you construct your array, you can do something like
cardContent.push(<div className="item-1" key="item-1">..</div>)
cardContent.push(<div className="item-2" key="item-2">..</div>)
I also want to mention for the example you've described, your elements are simple enough that having an inline condition rather than having two blocks that you conditionally return
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
{this.props.quotes && <i className="fa fa-quote-left"></i>}
{this.props.quotes && <i className="fa fa-quote-right"></i>}
</div>
)

React anchor link delims not parsing data

I have a simple anchor / for loop:
<ul className="user-list">
{this.props.partymembers.map((user, i) => {
return (
<li key={i} className="membername">
{user.username}
</li>
)
})}
</ul>
But the link just shows up as /friends/add/{user.id} instead of /friends/add/141513531 am I missing something here? Thanks. Username shows fine, but not inside the ""
I managed to solve it by doing
<li key={i} className="membername">
{React.createElement('a', { href: '/friends/add'+user.id}, user.username)}
</li>
but that almost seems hacky, is there a better way to do it?
The whole attribute needs to be either a string or an expression. So something like this:
<a href={'/friends/add/' + user.id}>{user.username}</a>
If you're using babel to transpile ES6, you can use template strings:
<a href={`/friends/add/${user.id}`>{user.username}</a>

Resources