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

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>

Related

Why tankStack React-table render the td tag 2 times after defining which component should render in column

I am facing a weird problem while working with tankStack react-table. I want render some specific tags with extra data. example: i want to render the age column like this (this is the {age})
But don't want to make the tag child of tag but that is happening
Here is my code
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return(
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
)
})
}
</tr>
))}
</tbody>
</table>
Column.js
export const COLUMNS = [
{
header:'First Name',
accessorKey:'first_name',
id:'first_name'
},
{
header:'Last Name',
accessorKey:'last_name',
id:'last_name'
},
{
header:'Age',
accessorKey:'age',
id:'age',
cell: data => <td>this is age{data.getValue()}</td>,
enableGlobalFilter: false,
}
]

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

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>
)
});

How do I display an array where each object in array is a new <td> in ReactJS

I have an array which contains multiple objects like this:
const locations = [
{
information: ["Data Analyst", "Uruguay", "Montevideo", "$30,000"],
},
{
information: ["CTO", "UK", "Manchester", "$75,000"],
}
];
And I want to display each array in a new table row containing a <td> with each string.
Currently I have done this:
<tr>
{locations.map((location) => <td>{location.information}</td>)};
</tr>
Which returns each piece of information on a <td>
This is how the data looks at the minute:
And how it's meant to look
For this you have to use Array.map() like:
// To create tha table body by iterating data array
{
locations.map((location, locationIndex) =>
<tr key={locationIndex}>
<td>{location.information[0]}</td>
<td>{location.information[1]}</td>
<td>{location.information[2]}</td>
<td>{location.information[3]}</td>
</tr>
)
}
// In case you don't want to harcode the array index
{
locations.map((location, locationIndex) =>
<tr key={locationIndex}>
location.information.map((loc, locIndex) =>
<td key={locIndex}>{loc}</td>
)
</tr>
)
}
Change the code from,
<tr>
{locations.map((location) => <td>{location.information}</td>)};
</tr>
To:
<table border="1" width="100%" className="component_useRefBox">
<tbody>
{locations.map((location, i) => {
return (
<tr key={i}>
{location.information.map((data, j) => {
return <td key={j}> {data} </td>;
})}
</tr>
);
})}
</tbody>
</table>
To display two records row by row, you need to move {locations.map((location) .... )} above <tr> ... </tr> tag, because while using map method only each row will get iterated and you will get two separate rows..
{locations.map((location, i) => {
return (
<tr>
....
</tr>
)})}
As location.information is of array data, you cannot assign it directly.
You need to iterate using map and display each data like,
{location.information.map((data) => {
return <td> {data} </td>;
})}

React how to render single element which got multiple child element in different <td> in table row

I am new to React. I am having the following block of code which is returned and sent to a component to render in html table. Currently i am using something like below. is there a better way
bodyItems = sorted.map((data) => [
data.employerName,
data.sectors.map((sector) => <div>{sector.name}</div>),
data.sectors.map((sector) => (
<div>
{sector.assignedLearners > 0
? `${sector.assignedLearners} learners`
: 'Unassigned'}
</div>
)),
data.lastModified
]
Rendering as:
bodyItems.map((entry, rowIndex) => (
<tr key={rowIndex}>
{entry.map((cell, colIndex) => (
<td key={colIndex}>{cell}</td>
))}
</tr>
))
Can anyone help how to render each div of the sectors in a separate in the component.
Expected output:
<tr>
<td>Employer Name</td>
<td>
<div>Sector 1</div>
<div>Sector 2</div>
</td>
<td>
<div>20 Learners</div>
<div>10 learners</div>
</td>
<td>
<!-- ... additional properties-->
</td>
</tr>
thanks
Are you saying you want to render each fragment you mapped from data.sectors into its own <td>? So you get a table structure like this?
<tr>
<td>Employer Name</td>
<td>
<div>Sector 1</div>
<div>2 learners</div>
</td>
<td>
<div>Sector 2</div>
<div>Unknown</div>
</td>
<!-- ... additional sectors -->
</tr>
If so, you need to flatten the array created with data.sectors.map, so you don't end up with a nested array. You can accomplish that with the spread operator (...) assuming your project supports it.
bodyItems = sorted.map((data) => [
data.employerName,
...data.sectors.map((sector) => (
<>
<div>{sector.name}</div>
<div>
{sector.assignedLearners > 0
? `${sector.assignedLearners} learners`
: 'Unassigned'}
</div>
</>
))
])
Alternatively, you could use flatMap.
bodyItems = sorted.flatMap((data) => [
data.employerName,
data.sectors.map((sector) => (
<>
<div>{sector.name}</div>
<div>
{sector.assignedLearners > 0
? `${sector.assignedLearners} learners`
: 'Unassigned'}
</div>
</>
))
])

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.

Resources