How to create dynamic star rating based on value in array object? - reactjs

I am trying to create a for loop inside td but i am getting error.
The product object has a rating, and I need to create a number of star using fontawesome based on the rating in the object
{
productId: 2,
productName: 'Garden Cart',
productCode: 'gdn 0011',
available: 'March 19, 2019',
price: '19.95',
rating: 4,
},
let product = this.state.products.map((x) => {
return (
<tr>
<td></td>
<td>{x.productName}</td>
<td>{x.productCode}</td>
<td>{x.available}</td>
<td>{x.price}</td>
<td>
<i class="fas fa-star"></i> //I need to create 4 star based on the object
</td>
</tr>
)
});

You can use an empty array with N element and map over it:
{[...new Array(4)].map(() => <i class="fas fa-star"></i>)}
Your code should look like this:
let product = this.state.products.map((x) => {
return (
<tr>
<td></td>
<td>{x.productName}</td>
<td>{x.productCode}</td>
<td>{x.available}</td>
<td>{x.price}</td>
<td>
{[...new Array(x.rating)].map(_ => <i className="fas fa-star"></i>)}
</td>
</tr>
)
});

Related

Mappin over an array of objects to display it in React

I am learning React, and I been stuck here for a while now, the Middle.js component and table. I am trying to loop over array of objects from the data I am fetching. It is userPools array and display in a table poolProvidersname and then consequently rest of the data.
I am quite confused as to how do I do this?
Here is the the sandbox: https://codesandbox.io/s/naughty-ellis-xrir4?file=/src/components/Table.js
Thank you.
jumping from JS to markup on JSX can bit a bit confusing at first
When iterating an array of objects for display purposes, what you want to do is return a JSX fragment for each item on your array.
So if you have an array that looks like this:
var data = [{ id: 1, name: 'Lucia', age: 20 }, { id: 2, name: 'Karina', age: 21 }, { id: 3, name: 'Maria', age: 22 }];
to display it in your component you would go like this:
const Table = ({ data }) => {
if (!Array.isArray(data)) return null;
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{
data.map(item => // notice how we jump straight back to JSX here
<tr key={item.id}>
<td>
{item.name}
</td>
<td>
{item.age}
</td>
</tr>)
}
</tbody>
</table>
);
};

React map array inside serialized DRF with a manytomanyfield relation and StringRelatedField

From my Django DRF serializer, I'm trying to map an array with an object in React.
serializer.py
class AssessmentSerializer(serializers.ModelSerializer):
objective = serializers.StringRelatedField(
many=True,
)
class Meta:
model = Assessment
fields = ['id', 'name', 'date_completed', 'objective']
The state from DRF looks ok:
{
grades: {
grades: [
{
id: 7,
name: 'Quiz 4',
date_completed: '2020-03-17',
objective: [
"kin.2",
"kin.1"
]
}
]
}
}
But I'm trying to separate out the kin.2 and kin.1 in my JSX table:
<tbody>
{this.props.grades.map(grade => (
<tr key={grade.id}>
<td>{grade.id}</td>
<td>{grade.name}</td>
<td>{grade.date_completed}</td>
<td>
<ul>
<li>{grade.objective}</li>
</ul>
</td>
</tr>
))}
</tbody>
Currently the 'kin.1' and 'kin.2' are printing on the same line with no space between the text. I've also tried:
<tbody>
{this.props.grades.map(grade => (
<tr key={grade.id}>
<td>{grade.id}</td>
<td>{grade.name}</td>
<td>{grade.date_completed}</td>
<td>
<ul>
{grade.objective.map(obj => (
<li>{obj.objective}</li>
))}
</ul>
</td>
</tr>
))}
</tbody>
This comes close, the list is empty although it iterates the correct number of times. Do I have to serialize some type of index with the 'objective'?
grade.objective is a list, so obj.objective wont work. Instead use obj
<tbody>
{this.props.grades.map(grade => (
<tr key={grade.id}>
<td>{grade.id}</td>
<td>{grade.name}</td>
<td>{grade.date_completed}</td>
<td>
<ul>
{grade.objective.map(obj => (
<li>{obj}</li>
))}
</ul>
</td>
</tr>
))}
</tbody>

how to map subarray by key of parent array reactjs

how to map subarray in react based on key of parent array?
I have tried mapping by id key of main array to map elements of dish_count array
<TableCell align="left">
{this.state.persons.map((items,name) =>
<div key={this.state.persons.id}>
{(typeof(items.dish_count)=="object")? (<div>
{ items.dish_count.map((subdata) =>
<table>
<td >
{subdata.meal_type}
</td>
</table>
)
}
</div>): (null)}</div>)}
</TableCell>
<TableCell align="left"> {this.state.persons.map((items,name) =>
<div key={this.state.persons.id} >{(typeof(items.dish_count)=="object")? (<div>
{
items.dish_count.map((subdata) =>
<table>
<td >
{subdata.dish_count}
</td>
</table>
)
}
</div>): (null)}</div>)}</TableCell>
i want to map subarray dish_count by key id of parent array .I am able to map but the mapping is multiple and is not exclusive by parent array key.dish_count is the subarray of package array
persons array
"data": [
{
"id": 1,
"name": "Gold",
"dish_count": [
{
"dish_count": 4,
"meal_type": "Starters"
},
{
"dish_count": 4,
"meal_type": "Main Course"
},
{
"dish_count": 4,
"meal_type": "Dessert"
},
{
"dish_count": 4,
"meal_type": "Lunch"
}
]
},
{
"id": 2,
"name": "Basic",
"dish_count": [
{
"dish_count": 2,
"meal_type": "Starters"
},
{
"dish_count": 2,
"meal_type": "Main Course"
},
{
"dish_count": 2,
"meal_type": "Dessert"
},
{
"dish_count": 2,
"meal_type": "Lunch"
}
]
}
]
I want
Meal Type No of Dishes
Gold Starters 4
Main Course 4
Desert 4
Lunch 4
Basic Starters 2
Main Course 2
Desert 2
Lunch 2
You have a few problems in your code:
this.state({persons:''});: This will set the initial persons to be an empty string and will fail with map.
console.log('package',this.state.persons): setState is async and the console.log will not print the desired state but the previous state. Use the callback of setState as second parameter to access the new state:
this.setState({persons:res.data.data.data}, () => console.log('package',this.state.persons) );
this.state.persons.map((items, name) =>: The map function will provide different parameters: the first is the person and the second is the index of that person within the array.
div key = {this.state.persons.id}: since persons is an array, the key will be undefined. If you use the map function correctly, you can use person.id.
When you fixed these problems, the code should work as expected.
To only show the expected dishes per person and not to print the duplication you have to write it like this:
class Table extends React.Component {
render() {
return <table>
<thead>
<tr>
<td>ID</td>
<td>Name</td>
<td>Meal Type</td>
<td>Number of Dishes</td>
</tr>
</thead>
<tbody>
{this.state.persons.map(person =>
<tr>
<td>{person.id}</td>
<td>{person.name}</td>
<td>
<table>
<tbody>
{person.dish_count.map(dish => <tr>
<td>{dish.meal_type}</td>
</tr>
)}
</tbody>
</table>
</td>
<td>
<table>
<tbody>
{person.dish_count.map(dish => <tr >
<td>{dish.dish_count}</td>
</tr>
)}
</tbody>
</table>
</td>
</tr>
)
}
</tbody>
</table >
}
}
By not iterating over the person for each sub-table, you can remove the duplicated data.
codepen
Hope this helps.

Angular : ng model array values got undefined in function on ng-click

I am trying to allow users to add quantity into text box. I have products array and each product contains its property. My service contains array list.
products = [{
id: 1,
name: 'X Product',
face: 'img/x_p.png',
available_size: 6,
color_available: 0,
sizes: ['10"', '20"', '30"'],
properties: [{size: '10"', mrp: '10$'},
{size: '20"', mrp: '15$'},
{size: '30"', mrp: '20$'}]
},
{
id: 2,
name: 'Y Product',
face: 'img/y_p.png',
available_size: 6,
color_available: 0,
sizes: ['10"', '20"', '30"'],
properties: [{size: '10"', mrp: '10$'},
{size: '20"', mrp: '15$'},
{size: '30"', mrp: '20$'}]
}]
Iterating through the products array. When user clicks on product it will display a product show page. where I am showing all the information for the product. For properties I am showing a table where user can add their quantity just after each size.
<div style="margin-bottom: 20px;">
<h4>
Product Detail
</h4>
<table>
<tbody>
<tr class="tableHeader">
<td>Size</td>
<td>MRP</td>
<td>Quantity</td>
</tr>
<tr ng-repeat="(key, property) in product.properties">
<td>{{ property['size'] }}</td>
<td>{{ property['mrp'] }}</td>
<td>
<input type="number" placeholder="QTY" ng-model="qty">
</td>
<td>
<a href="javascript:;" class="button" ng-click="addToCart(property['size'], qty)">
Add
</a>
</td>
</tr>
</tbody>
</table>
</div>
Question: So according to this table each row contains a button with name add. So after adding quantity user clicks on add button will them allow to add item in cart. here I am getting size in addToCart function but got undefined qty as it should be an array with its respective size. Though I am getting size. But I don't know how to do that.
$scope.addToCart = function(size, qty) {
alert($scope.qty)
}
I need a help.
var app = angular.module("MyApp", []);
app.controller("MyCtrl", function() {
this.addToCart = function(property, qty) {
property["qty"] = qty;
}
this.product = {
id: 1,
name: 'X Product',
face: 'img/x_p.png',
available_size: 6,
color_available: 0,
sizes: ['10"', '20"', '30"'],
properties: [{
size: '10"',
mrp: '10$'
}, {
size: '20"',
mrp: '15$'
}, {
size: '30"',
mrp: '20$'
}]
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div style="margin-bottom: 20px;" ng-app="MyApp">
<div ng-controller="MyCtrl as ctrl">
<h4>
Product Detail
</h4>
<table>
<tbody>
<tr class="tableHeader">
<td>Size</td>
<td>MRP</td>
<td>Quantity</td>
</tr>
<tr ng-repeat="(key, property) in ctrl.product.properties">
<td>{{ property['size'] }}</td>
<td>{{ property['mrp'] }}</td>
<td>
<input type="number" placeholder="QTY" ng-model="qty">
</td>
<td>
<a href="javascript:;" class="button" ng-click="ctrl.addToCart(property, qty)">
Add
</a>
</td>
</tr>
</tbody>
</table>
<hr/>
{{ctrl.product.properties}}
</div>
</div>
I have noticed many times that the textbox won't bind to a normal scope variable like
$scope.qty
and binding it in view with ng-model="qty"
Please try using something like
$scope.qty = {
text:' '
}
and bind it as ng-model="qty.text" in the view
which will make sure that your textbox is actually bound.
Simple thing to fix this issue: init qty by $scope.qty = 0;

Ng-repeat doesn't works with localStorage

Something weird is happening with my localStorage. Data is saved in my localStorage but is never displayed in ng-repeat. However, when the data is less than 2 items, in that case, it is displayed on the screen. When it is greater than 2 it is not shown. This is my controller code
$scope.saved = localStorage.getItem('todos');
if($scope.saved != null ) {
$scope.invoice = { items: JSON.parse($scope.saved) };
} else {
$scope.invoice = { `enter code here`
items : [{
qty: 10,
description: 'item',
cost: 9.95}]
};
}
And this is my addition code:
$scope.addItem = function(x) {
$scope.invoice.push({
qty: 1,
description: x.cakename,
cost: x.id
});
localStorage.clear();
localStorage.setItem('todos', JSON.stringify($scope.invoice));
};
This is table code:
<div>
<table class="table" style="margin-top:50px">
<tr>
<th>Name</th>
<th>Qty</th>
<th>Cost</th>
<th>Total</th>
<th></th>
</tr>
<tr ng-repeat="item in invoice">
<th>#{{ item.description }}</th>
<th>#{{ item.qty }}</th>
<th>#{{ item.cost }}</th>
<th>#{{ item.qty * item.cost }}</th>
<td>[<a href ng:click="removeItem($index)">X</a>]</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Total:</td>
<td> #{{ total() | currency }} </td>
</tr>
</table>
<a href="#/checkout" class="btn btn-danger">
Checkout <span class="glyphicon glyphicon-shopping-cart" aria-hidden="true"></span>
</a>
</div>
When there are only 2 items and I refresh, the screen shows,
Name Qty Cost Total
Black Forest 1 1 1 [X]
Berry 1 2 2 [X]
Total: $3.00
Checkout
otherwise, it shows
Name Qty Cost Total
Total: $7.00
As the total is 7.. The data is still there but not getting displayed.
You stringify the entire invoice object, but when you read it back from local storage, you parse it into an object with a different structure.
Try $scope.invoice = JSON.parse($scope.saved) instead.
I solved my question. It was because of the duplicate keys in the array. Duplicate keys are banned because AngularJS uses keys to associate DOM nodes with items. So adding this did the trick.
ng-repeat="item in invoice track by $index"
So stupid of me!

Resources