Iterate through a nested json object array in the render (ReactJS) - json

I'm trying to iterate and display certain values of a JSON on my web application using ReactJS.
My render looks like this:
render() {
const orders =
Object.keys(this.state.data).map((e,i) => {
return (
<div key = {i}>
<div>ID: {this.state.data[e].id}</div>
<div>Email: {this.state.data[e].email}</div>
<div>Note: {this.state.data[e].note}</div>
<div>{this.findValue(e)}</div>
</div>
)
})
return (
<div>
<div>
{orders}
</div>
</div>
);
}
Now everything looks fine until I run this.findValue where it immediately returns after the first iteration instead of returning multiple divs.
findValue = (e) => {
for(let key2 in this.state.data[e].line_items) {
return (
<div>
Line item: {this.state.data[e].line_items[key2].title}
</div>
)
}
How would I be able to return every line item? Thanks in advance.

When you return something from the function given to map, you are just returning what the current element should be mapped to in the new array. When you return inside the for..in, you are returning from the findValue method instead.
You could use map on the array of keys in line_items as well.
findValue = e => {
const { line_items } = this.state.data[e];
return Object.keys(line_items).map(key => (
<div key={key}>Line item: {line_items[key].title}</div>
));
};

Related

React loop through json object and display data

I have a demo here
I have a simple json file that I'm importing and I would like to loop through and output the json data in a div
I'll probable want to pick out parts of the json but for now I just need to be able to output the json
Do I need to create an array from the json data and then map over that.
const showProductData = Object.keys(ProductData).map(function(key) {
return <div>{ProductData[key]}</div>;
});
const App = () => {
return (
<div>
<h2>JSON</h2>
{showProductData}
</div>
);
};
If you read the error message, Objects are not valid as a React Child. To modify your current code to just show the json, you will need to convert the object into a string.
const showProductData = Object.keys(ProductData).map(function(key) {
return <div>{JSON.stringify(ProductData[key])}</div>;
});
To be more concise with what we're accessing, we can instead use Object.values() instead:
const showProductData = Object.values(ProductData).map(function(value) {
return <div>{JSON.stringify(value)}</div>;
});
To further access specific points of the data, you can use dot notation to access primitive values:
const showProductData = Object.values(ProductData).map(function(value) {
return <div>Weight: {value.ProductWeight}</div>;
});
well, when i show ur a question, immediately i thought 'recursive solution' :)
so basically this is the code, I tried to explain it, feel free to dig into it
function getAllProps(obj) {
let value = obj;
// in case it is an object / array, which true, at any json file, at least at the beginning
if (typeof obj === "object") {
value = Object.keys(obj).map(key => {
// and then check again if the value of the 'key' is an object
// because we cant just out put object in react
if (typeof obj[key] === "object") {
// then out put the key name (property, 'ProductOne' for example)
// and call the function again since we know it is an object.
// basiclly the function will call it self again and again
// until the value will be different than 'object'
return (
<div key={key}>
<div style={{ marginLeft: 20 }}>
<strong> {key} </strong>
{getAllProps(obj[key])}
</div>
</div>
);
}
return (
<div key={key} style={{ marginLeft: 20 }}>
{key}: {obj[key]}
</div>
);
});
}
return value;
}
const products = getAllProps(ProductData);
const App = () => {
return (
<div>
<h2>JSON</h2>
{products}
</div>
);
};
actually, just check that link
read the comments, try to understand my 'recursive solution'

Replace one element in an Array of Angular model objects with another object using Angular2

I have an array of angular objects. I want to replace an entire element in the array with another element.
export interface modelCourse{
var1: number;
var2: number;
}
I want to do something like this:
updateResults(myCourse: modelCourse) {
this.allCourses.forEach(element => {
if (myCourse.Id=== element.Id) {
element = myCourse; // this part doesn't work as expected
}
});
}
Where
allCourses: modelCourse[] = [];
And allCourses holds all my courses.
If you only need to find the one matching element, you don't need to loop through all of the elements, just use the findIndex method. This finds the index of the one element you need without looping through all of them.
You can then use that index to update the original array.
updateResults(myCourse: modelCourse) {
const foundElementIndex = this.allCourses.findIndex(element => myCourse.id === element.id);
this.allCourses[foundElementIndex] = myCourse;
}
I did a stackblitz here: https://stackblitz.com/edit/angular-hxxddn
Please use Array.map to update this.allCourses.
updateResults(myCourse: modelCourse) {
this.allCourses = this.allCourses.map(element => {
if (myCourse.Id === element.Id) {
return myCourse;
}
return element;
});
}

React--Div exists, but is empty & more problems

I'm using the code below to pull in a list of data from a JSON file in order to populate a webpage with News. However, with what I have, the div is empty when I inspect it, and I'm not sure why. When I attempt other solutions, I get errors or the same output.
const newsList = labNewsJson['news']
class News extends Component {
render() {
const news = newsList.map((newsItem) => {
<div>{newsItem}</div>
});
return (
<div className='container'>
<h1>Lab News</h1>
<div>{news}</div>
</div>
);
}
}
export default News;
You need to add a return to your map function.
const news = newsList.map((newsItem, index) => {
return <div key={index}>{newsItem.title}</div>
});
When you are using {}, map function does not return anything. You have two options:
1- Try to use () instead of {}:
const news = newsList.map((newsItem) => (
<div>{newsItem}</div>
))
2- Return the item in every iteration:
const news = newsList.map((newsItem) => {
return <div>{newsItem}</div>
})

using flatmap to make nested service call with parameter

I am making a service call returning data from json file with a bunch of items but after get all the items i need to make another service call to get the contents of each of the items. I am using a flatmap but i am having some trouble on how to pass in a parameter - when i try it becomes underlined in the code as an error.
This is my data call:
getItems(){
this.itemService.getItemsData().flatMap(
data => {this.items = data;
error => this.errorMessage = <any>error;
return this.itemService.getItemContent();
}).subscribe(data => {
this.itemContent = data;
});
}
when i try passing into...getItemContent(this.items.contentUri) it gives me an error.
getItemsData(){
return this._http.get(url)
.map((res:Response) => res.json())
.map((obj) => Object.keys(obj).map((key)=>{return obj[key]}))
.catch(this.handleError);
}
getItemContent(uri){
return this._http.get(uri)
.map((res:Response) => res.json())
.catch(this.handleError);
}
How should i properly do this so when i get items i could also make a call to get the items contents based on a parameter?
here is a sample of the json structure:
{
Item 1: {
title:....
id:....
content:{
uri:"link"
}
}
}
UPDATE:
getItems(){
this.itemService.getItemsData().flatMap(
data => {this.items = data;
for(let x of data){
var url = x.content.uri;
this.observables.push(this.itemService.getInnerItemData(url));
}
return Observable.forkJoin(this.observables);
}).subscribe(data => {
this.itemsContent = data;
});
}
HTML:
<div *ngFor="let item of items">
{{item.title}}
<div *ngFor="let content of itemsContent">
{{content.infoText}}
</div>
</div>
now within my display the item.title is showing appropriately as expected but the content within each item is showing up as an array of [object][object] and seems like all the itemsContent is showing up for each item and it is not specified with each itemsContent with belonging item.
Use forkJoin to make parallel requests.
getItems(){
this.itemService.getItemsData().flatMap(
data => {
this.items = data;
var observables = [];
for(let x of data){
var url = x.content.uri;
observables.push(this.itemService.getItemContent(url));
}
return Observable.forkJoin(observables);
}).subscribe(data => {
console.log(data);
});
}
Then in your subscribe you will see an array of response for your item contents.
Update
In your view, you can track the items' index and display that indexed items content like this:
<div *ngFor="let item of items; let i = index;">
{{item.title}}
<dic>
{{itemsContent[i]?.infoText}}
</div>
</div>

ReactJS ES6 adding logic to the component

I have a chat feature and I'm trying to display an array of messages into my JSX code with a conditionally rendered className depending on a value in an object.
I'm really new to ES6 and React and I cannot figure how to go about this logic inside the JSX. I am using redux to map my state into props. For brevity, I've cut the code into its simplest form.
class ChatWidget extends Component {
render() {
return (
<div className={chat.body}>
{/* if from_id == 1 */}
<div className={chat.float-right}>
<p> {/* message of from_id==1 */} </p>
</div>
{/* else */}
<div className={chat.float-left}>
<p> {/* else message here */} </p>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
messages: state.messages
};
}
Here is my sample JSON array.
[
{
msg_id: 1,
to_id: 1,
from_id: 2,
message_id: "Marzipan jelly-o croissanty"
},
{
msg_id: 2,
to_id: 2,
from_id: 1,
message_id: "Jelly sw"
}
]
You can use your messages array to create an array of JSX elements which can be used in another JSX expression for rendering.
Since you want the message with from_id == 1 to appear first you can use Array#sort to order the messages array (to avoid mutating messages a shallow copy of the array is made using Array#slice).
You then then call Array#map on the newly returned array to iterate through the sorted messages creating new JSX on each iteration.
The code could look something like this:
class ChatWidget extends Component {
render() {
// deconstruct messages from props
const {messages} = this.props;
// Mutating data should be avoided and Array#sort mutates
// the array so we use Array#slice to create a
// shallow copy before we sort it
const msgCopy = messages.slice();
// sort messages so message where from_id === 1 is first
const sorted = msgCopy.sort((a,b) => {
return a.from_id - b.from_id;
});
// iterate through sorted array to create
// an array JSX elements
sorted.map(message => {
const {from_id, message_id} = message;
// Change class if from_id is 1
const classToAdd = (from_id === 1) ? ('chat.convo + ' ' + chat.them'):('chat.convo');
return (
<div className={classToAdd}>
<div className={chat.text}>
<p> { message_id } </p>
</div>
</div>
);
});
// use sorted array as follows to create a div containing
// a child div for each message
return
(
<div className={chat.body}>
{sorted}
</div>
);
}
}
function mapStateToProps(state) {
return {
messages: state.messages
};
}