I am using react (Im not too experienced with it) and want to render data, the problem is everything is nested - with objects and arrays. I have tried so many different things but its just not working.
For example: the data is flight info. For a round trip I have a nested array for each connecting flight and the same back. For other things like departure airport-its again nested differently. Since I want to display multiple flights, I have to iterate through all that when I render the date but I don't know how to. I tried to find something online and have been pretty much staring at my code for a while and I am pretty much lost. If anyone could help, it would be great. I also added the json with the data (I removed some key value pair and left those which are important for the nested/different data structure). Thanks a lot!
data:
{
"PricedItineraries": [{
"AirItinerary": {
"OriginDestinationOptions": {
"OriginDestinationOption": [{
"FlightSegment": [{
"DepartureAirport": {
"LocationCode": "JFK"
},
"ArrivalAirport": {
"LocationCode": "MSP"
},
"DepartureDateTime": "2018-01-07T07:00:00",
"OperatingAirline": {
"FlightNumber": 111,
"Code": "AA"
}
},
{
"DepartureAirport": {
"LocationCode": "MSP"
},
"ArrivalAirport": {
"LocationCode": "LAX"
},
"DepartureDateTime": "2018-01-07T10:05:00",
"OperatingAirline": {
"FlightNumber": 444,
"Code": "SY"
}
}],
"ElapsedTime": 485
},
// ... same structure below for trip back ...
{
"FlightSegment": [{
"DepartureAirport": {
"LocationCode": "LAX"
},
"DepartureTimeZone": {
"GMTOffset": -8
}
},
{
"DepartureAirport": {
"LocationCode": "SEA"
},
"DepartureTimeZone": {
"GMTOffset": -8
}
}],
"ElapsedTime": 745
}]
},
"DirectionInd": "Return"
},
"AirItineraryPricingInfo": {
"PTC_FareBreakdowns": {
"PTC_FareBreakdown": {
"PassengerTypeQuantity": {
"Quantity": 1,
"Code": "ADT"
},
"Endorsements": {
"NonRefundableIndicator": true
}
}
},
"FareInfos": {
"FareInfo": [{
"TPA_Extensions": {
"SeatsRemaining": {
"BelowMin": false,
"Number": 4
}
}
}
}]
},
"ItinTotalFare": {
"TotalFare": {
"CurrencyCode": "USD",
"DecimalPlaces": 2,
"Amount": 341.61
},
"Taxes": {
"Tax": [{
"CurrencyCode": "USD",
"DecimalPlaces": 2,
"TaxCode": "TOTALTAX",
"Amount": 66.25
}]
}
}
},
"TicketingInfo": {
"TicketType": "eTicket"
}
}, {
"AirItinerary": {
..same structure again...repeats multiple times
React Component:
class Search extends React.Component {
constructor(props){
super(props);
this.state={
origin:'',
destination:''
...
// flightinfo
airlineCodeToDestination:[],
airport:[],
departureTime:[],
arivalTime:[],
totalDuration:[],
price:[],
flightInfo:[]
}
this.handleInput=this.handleInput.bind(this);
this.testing=this.testing.bind(this);
}
testing(e){
this.setState({
[e.target.name]:e.target.value
})
}
handleInput(e){
e.preventDefault();
regularSearchData(this.state.origin, this.state.destination, this.state.departuredate, this.state.returndate)
.then(function(response){
return response.data.PricedItineraries;
})
.then(res => {
let response=res,
allFares=[],
airlineCode=[],
airport=[],
x=[],
departureTime=[],
arivalTime=[],
totalDuration=[];
response.map(function(item){
//this here are just a few of my tries
allFares.push(item.AirItineraryPricingInfo.ItinTotalFare.TotalFare.Amount);
x.push(item.AirItinerary.OriginDestinationOptions.OriginDestinationOption);
airlineCode.push(item.AirItinerary.OriginDestinationOptions.OriginDestinationOption[0].FlightSegment[0].MarketingAirline.Code);
});
this.setState({
price:allFares,
airlineCodeToDestination:airlineCode,
flightInfo:x
})
})
}
render () {
const flights = this.state.flightInfo.map((item, i) => {
return (
<div>
<div key={i}> {item} </div>
</div>);
});
return (
<div>
{flights}
<form onSubmit={this.handleInput}>
<input type="text" name="origin" value={this.state.origin} onChange={this.testing} />
<input type="text" name="destination" value={this.state.destination} onChange={this.testing}/>
<input type="date" name="departuredate" value={this.state.departuredate} onChange={this.testing} />
<input type="date" name="returndate" value={this.state.returndate} onChange={this.testing} />
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
export default Search;
In your handleInput method, you are creating new arrays and adding data to them.Then you are calling setState to set these new arrays as your new state, which will result in the old state getting removed and only the new arrays showing up.
If you want your old data to persist, you would need to change the declaration of the variables in the code as follows:
....
allFares=[...this.state.allFares],
airlineCode=[...this.state.airlineCode],
....
This will create copies of the exiting arrays from your state and when you push your new item in them and then call setState to set them, you will not lose your existing data.
Related
I am desperately trying to generate multiple select inputs for a given JSON from an Backend but I cant make it work. The JSON response I am getting looks smth like this:
{
"selectData": [
{
"id": "ats_2323680",
"label": "Follow up",
"value": "option_id_1"
},
{
"id": "ats_2323701",
"label": "1st Interview, Client",
"value": "option_id_1"
},...
],
"optionData": {
"texts": [
"Sourced",
"On hold",
...
],
"values": [
"option_id_1",
"option_id_2",
]
}
}
Ive already tried several ways and my last attempt looks like this:
Template:
<div v-for="select in selectData" :key="select.id">
<p>{{ select.label }}</p>
<v-select
:items="optionData.texts"
:value="getOptionById(select.value)"
#input="(id) => updateSelect(select, id)"
></v-select>
</div>
Script:
<script>
export default {
data() {
return {
selectData: [],
optionData: {
values: [],
texts: [],
},
};
},
methods: {
fetchData() {
const headers = this.authorizationHeader;
axios
.get(url,
{
headers,
}
)
.then((response) => {
let data = response.data;
this.selectData = data.selectData;
this.optionData = data.optionData;
})
.catch((error) => console.log(error));
},
updateSelect(select, id) {
select.value = id;
},
getOptionById(id) {
let i = this.optionData.values.findIndex((x) => x === id);
return this.optionData.texts[i];
},
},
mounted() {
this.fetchData();
},
};
</script>
I am also not super happy with the JSON struct I am getting. The reason that the optionTextId is also send is, that the optionTexts will be in different languages.
I am really happy with any advise.
I think I solved it. I think it was a classy case of overthinking.
First I changed the JSON structure in the backend like:
{
"selectData": [
{
"id": "ats_2323680",
"label": "Follow up",
"text": "Sourced",
},
{
"id": "ats_2323701",
"label": "1st Interview, Client",
"text": "Kandidaten nachgefasst",
},
...
],
"optionData": {
"texts": [
"Sourced",
"Kandidaten kontaktiert",
...
],
"values": [
"option_id_1",
"option_id_2",
...
]
}
}
Then I changed the Vue code to:
Template:
<div v-for="select in selectData" :key="select.id">
<label for="location">{{ select.label }}</label>
<select id="location" name="location" v-model="select.text">
<option
v-for="option in optionData.texts"
:key="option"
:value="option"
>
{{ option }}
</option>
</select>
</div>
Script:
<script>
export default {
data() {
return {
selectData: [],
optionData: {
values: [],
texts: [],
},
};
},
methods: {
fetchData() {
const headers = this.authorizationHeader;
axios
.get(
url,
{
headers,
}
)
.then((response) => {
let data = response.data;
this.selectData = data.selectData;
this.optionData = data.optionData;
})
.catch((error) => console.log(error));
},
},
mounted() {
this.fetchData();
},
};
</script>
Apparently changing the JSON struc and using v-model did the magic. Might be obvious. Hope this helps a lost soul like me at some point :)
I am creating a product web-app by using vue-2.6.11, axios-0.21.1, vuetify-2.4.3
I am fetching categories from local array then I am passing fetchUrl as Props it into Row component by using v-for . Then in Row component i am fetching the fetchUrl by using axios after getting API response I'm simply mounting it. It working fine but the problem is categories object means Row component loads in random order cause the Row component mounted as it got axios response from API.
So I want Next row await till upper fully-mounted or any thing else to make it orderly loaded.
My Components :
Home.vue -
<template>
<div>
<div v-for="(categories,index) in categories" :key="`${index}`">
<ItemsCarousel
:title="categories.title"
:fetch-url="categories.fetchUrl"
/>
</div>
</div>
</template>
<script>
import categoriesList from '#/local-database/Categories.json';
import ItemsCarousel from '#/components/carousel/ItemsCarousel';
export default {
name: 'Home',
components: {
ItemsCarousel
},
data: () => ({
categories: categoriesList.filter( categories => (catalogue.for==true || categories.for=="home"))
})
}
</script>
ItemsCarousel.vue -
<template>
<div class="items-carousel">
<v-lazy v-model="isActive" :options="{threshold: 0.5}">
<h1>{{title}}</h1>
<div class="items-carousel" v-for="product in products" :key="product.id">
<Card v-bind="{...product}">/>
</div>
</v-lazy>
</div>
</template>
<script>
import ProductManger from '#/mixins/ProductManger';
import Card from '#/components/Card';
export default {
name: 'ItemsCarousel',
mixins: [ProductManger], // Axios Setup
components: {
Card
},
props: ['title','params'],
data: () => ({
isActive: false,
cards: []
}),
methods: {
async loadCard() {
this.contentMangerCore(this.params) // Function code inside mixins
.then(res => {
this.cards = res.data;
})
}
},
mounted() {
this.loadCard();
}
};
</script>
DataSample :-
categoriesList.json-
[{
"id": 1,
"name": "Adventure",
"params": {
"categories": "Adventure",
"sort": "ASC"
}
}, {
"id": 2,
"name": "Art",
"params": {
"categories": "Art",
"sort": "DESC"
}
}, {
"id": 3,
"name": "Beauty",
"params": {
"categories": "Art",
"sort": "DESC"
}
}, {
"id": 4,
"name": "Business",
"params": {
"categories": "Art",
"sort": "DESC"
}
}, {
"id": 5,
"name": "Craft",
"params": {
"categories": "Art",
"sort": "DESC"
}
},...]
products.json-
[{
"name": "AdventureIRC",
"img": "..."
},
{
"name": "Adventie",
"img": "..."
},...]
I Hope you guys will help me to resolve this...
Thank You :smile:
You could make a computed method that determines how many categories to actually display at any given time, incremented by successful axios requests.
get categoriesForDisplay() {
return this.categories.slice(0, this.successfulCarouselFetches + 1)
}
Then define successfulCarouselFetches :
data() {
return {
//
successfulCarouselFetches : 0
}
}
listen for successful axios requests in your Item-Carousel component:
<ItemsCarousel
:title="categories.title"
:fetch-url="categories.fetchUrl"
#success="successfulCarouselFetches = successfulCarouselFetches + 1"
/>
Now broadcast the success whenever your xhr is done working:
methods: {
async loadCard() {
this.contentMangerCore(this.params) // Function code inside mixins
.then(res => {
this.cards = res.data;
this.$emit('success');
})
}
},
When the page loads you'll have a single Item-Carousel component on the page which will perform the first XHR request. When the component $emit's the event, the parent component containing the v-for will increment the successfulCarouselFetches which will allow the getter categoriesForDisplay to add another Item-Carousel within the v-for.
This essentially performs what you're asking for, I believe.
There is a page where you can see the details of a user's loan. There is a decorator where I return values using the get () method. In general, there is a partial refund, which returns items of partial payments as shown in the photo. My problem is that I cannot specify all partial payments, only receive one by one.
Loan Details component:
<div className="ClientLoanDetails__card__content__inner__wrapper">
{Object.keys(payments[0]).map(val => {
{
[payments[0][val]].map((payment: any, index: number) => (
<div className="ClientLoanDetails__card__content-inner" key={index}>
{paymentsFields.map((item, indexInner) => (
<div className="ClientLoanDetails__card__content-item" key={indexInner}>
<div className="ClientLoanDetails__card__content-item__title">
{item.title}
</div>
<div className="ClientLoanDetails__card__content-item__value">
{payment[item.key]}
</div>
</div>
))}
</div>
));
}}
)
}}
</div>
This is code snippet for key & titles from loan.ts:
export const repaymentsFields = [
{
key: 'issuedDate',
title: lang.CLIENTS.REPAYMENTS.ISSUED_DATE,
},
{
key: 'period',
title: lang.CLIENTS.REPAYMENTS.PERIOD_IN_DAYS,
},
]
JSON of repayments:
"partialRepayments": [
{
"orderId": "A11Fz090VT1BmObJ0S-0",
"repaidPrincipalAmount": {
"amount": 250000.0
},
"repaidInterestAmount": {
"amount": 0
},
"repaidOverdueAmount": {
"amount": 0
},
"repaidProlongationAmount": {
"amount": 0
},
"started": "2020-11-09T16:52:08.981+0600",
"completed": "2020-11-09T16:52:21.170+0600",
"period": 25,
"timestamp": "2020-11-09T16:52:21.174+0600"
},
{
"orderId": "A11Fz090VT1BmObJ0S-1",
"repaidPrincipalAmount": {
"amount": 300000.0
},
"repaidInterestAmount": {
"amount": 0
},
"repaidOverdueAmount": {
"amount": 0
},
"repaidProlongationAmount": {
"amount": 0
},
"started": "2020-11-09T16:54:31.923+0600",
"completed": "2020-11-09T16:54:46.313+0600",
"period": 25,
"timestamp": "2020-11-09T16:54:46.317+0600"
}
],
the problem is that it is impossible to display the values that come as in the photo (one loan may have several repayments)
I have to return all values from an Object
IMAGE of console
Why don't you loop through the partialPayementResponse and access each field through the dot . notation ? Such as follows:
<div>
{partialRepayments.map((item, index) => {
return (
<div key={index}>
<div>Completed: <span>{item.completed}</span></div>
<div>orderId: <span>{item.orderId}</span></div>
<div>period: <span>{item.orderId}</span></div>
<div>repaidInterestAmount: <span>{item.repaidInterestAmount.amount}</span></div>
<div>repaidOverdueAmount: <span>{item.repaidOverdueAmount.amount}</span></div>
<div>repaidPrincipalAmount: <span>{item.repaidPrincipalAmount.amount}</span></div>
<div>repaidProlongationAmount: <span>{item.repaidProlongationAmount.amount}</span></div>
<div>started: <span>{item.started}</span></div>
<div>timestamp: <span>{item.timestamp}</span></div>
</div>)
})}
</div>
I wrote it without using your classNames but I think you get the idea
Not sure what's the problem here.
Can't u just iterate over these things?
Or create function and then display the thing that function returns...
for example:
displayAppropriateFields = () => {
let returnedArray = [];
returnedArray.push(<div> Some text in the div </div>);
for(let i=0; i<someArray.length; i++) {
returnedArray.push(<span> someArray[i] </span>)
}
return returnedArray;
}
And then display it in render method like so:
this.displayAppropriateFields();
I'm a newbie in Vue-js and really need your help:
In my Django project I have 2 models: Patient and MedCard of this patient. They are connected with a Foreign Key. I want to implement such functionality: on page "Patients" I have list of patients, then when I push on someone's name I want to see his/her MedCard.
This is my code, but when I push on name I get all records for all patients from MedCard model:
Patients.vue:
<div v-for="patient in patients">
<h3 #click="openMedCard(patient.id)">{{patient.surname}} {{patient.name}}</h3>
<p>{{patient.birth_date}}</p>
</div>
<div
<MedCard v-if="med_record.show" :id="med_record.id"></MedCard>
</div>
export default {
name: 'Patient',
components: {
MedCard,
},
data() {
return {
patients: '',
med_record: {
patient: '',
show: false,
}
}
}
and methods from Patient.vue:
methods: {
openMedCard(id) {
this.med_record.patient = id
this.med_record.show = true
}
MedCard.vue:
<template>
<mu-row v-for="med_record in med_records">
<h3>Doc – {{med_record.doc.surname}}{{med_record.doc.name}}</h3>
<p>{{med_record.patient.surname}}</p>
<p>{{med_record.record}}</p>
<small>{{med_record.date}}</small>
</mu-row>
</template>
export default {
name: 'MedCard',
props: {
id: '',
},
data() {
return {
med_records: '',
}
},
methods: {
loadMedCard() {
$.ajax({
url: "http://127.0.0.1:8000/api/v1/hospital/med_card/",
type: "GET",
data: {
id: this.id,
patient: this.patient
},
success: (response) => {
this.med_records = response.data.data
}
})
}
}
}
loadMedCard() gives me info from all MedCards in JSON like this:
{
"data": {
"data": [
{
"id": 1,
"patient": {
"id": 1,
"surname": "KKK",
"name": "KKK",
"patronymic": "LLL",
"birth_date": "1999-07-07",
"sex": "F",
"phone": "no_phone",
"email": "no_email"
},
"doc": {
"id": 3,
"surname": "DDD",
"name": "DDD",
"patronymic": "DDD",
"education": "d",
"category": "2",
"sex": "m",
"phone": "no_phone",
"email": "no_email"
},
"record": "test text",
"date": "2020-06-09"
}...]
I'll be grateful for any help!
So the API returns you multiple patients's data while you're asking it for just one exact patient. There must be something wrong with the API with the filtering in first place. So you can filter your data on the client side, in your MedCard.vue component. First this component have to show data for one patient only, so the v-for="med_record in med_records" is not needed. Your med_records property can become just an object not an array:
data() {
return {
med_record: {},
}
}
And in the success resolve method of your API call you can filter only the data you need and store it in med_record
success: (response) => {
this.med_records = response.data.data.find((patient)=> { return patient.id === this.id})
}
If you want to store all the data in the med_records, then you can create computed property and apply the same filtering there.
I hope this helps.
I have a scrolling menu items, and the titles of each item is hardcoded into a const, along side with the id
const list = [
{ name: "category1", id: 0 },
{ name: "category2", id: 1 },
{ name: "category3", id: 2 },
{ name: "category4", id: 3 },
{ name: "category5", id: 4 },
{ name: "category6", id: 5 },
{ name: "category7", id: 6 },
{ name: "category8", id: 7 }
];
I have a json file that contains the category name for each child:
{
"results": [
{
"category": "category1",
"name": {
"title": "mr",
"first": "ernesto",
"last": "roman"
},
"email": "ernesto.roman#example.com",
"id": {
"name": "DNI",
"value": "73164596-W"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/73.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/73.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/73.jpg"
}
},
{
"category": "category2",
"name": {
"title": "mr",
"first": "adalbert",
"last": "bausch"
},
"email": "adalbert.bausch#example.com",
"id": {
"name": "",
"value": null
} etc....
I want to show these categories "category": "category1", as the titles of my menu, I now that I need to start stateless and add them from the JSON, the fetching part from the JSON is done locally in componentDidMount, but I am not sure how can I map them into appearing as menu names to make the menu dynamic, I basically want the same output but from the json not hardcoded. here is a sandbox snippet, would appreciate the help.
https://codesandbox.io/s/2prw4j729p?fontsize=14&moduleview=1
Just convert the JSON output to an object like list with a map function from the results and then set is as MenuItems on the state, which is what you pass to the function on render(). Like that.
import React, { Component } from "react";
import ScrollMenu from "react-horizontal-scrolling-menu";
import "./menu.css";
// One item component
// selected prop will be passed
const MenuItem = ({ text, selected }) => {
return (
<div>
<div className="menu-item">{text}</div>
</div>
);
};
// All items component
// Important! add unique key
export const Menu = list =>
list.map(el => {
const { name, id } = el;
return <MenuItem text={name} key={id} />;
});
const Arrow = ({ text, className }) => {
return <div className={className}>{text}</div>;
};
export class Menucat extends Component {
state = {
selected: "0",
MenuItems: []
};
componentDidMount() {
fetch("menu.json")
.then(res => res.json())
.then(result => {
const items = result.results.map((el, idx) => {
return { name: el.category, id: idx };
});
this.setState({
isLoaded: true,
MenuItems: items
});
});
}
render() {
const { selected, MenuItems } = this.state;
// Create menu from items
const menu = Menu(MenuItems, selected);
return (
<div className="App">
<ScrollMenu
data={menu}
selected={selected}
onSelect={this.onSelect}
alignCenter={true}
tabindex="0"
/>
</div>
);
}
}
export default Menucat;
Cheers!
Looks like you don't have to hard code your category list at all. In your componentDidMount() fetch the json and group the results into separate categories like this:
const json = {
"results": [
{
category: "category1",
name: "Fred"
},
{
category: "category1",
name: "Teddy"
},
{
category: "category2",
name: "Gilbert"
},
{
category: "category3",
name: "Foxy"
},
]
}
const grouped = json.results.reduce((acc, cur) => {
if (!acc.hasOwnProperty(cur.category)) {
acc[cur.category] = []
}
acc[cur.category].push(cur)
return acc;
}, { })
// parent object now has 3 properties, namely category1, category2 and category3
console.log(JSON.stringify(grouped, null, 4))
// each of these properties is an array of bjects of same category
console.log(JSON.stringify(grouped.category1, null, 4))
console.log(JSON.stringify(grouped.category2, null, 4))
console.log(JSON.stringify(grouped.category3, null, 4))
Note that this json has 4 objects in result array, 2 of cat1, and 1 of cat 2 and cat3. You can run this code in a separate file to see how it works. Ofcourse you will be fetching the json object from server. I just set it for demonstration.
Then set teh state:
this.setState({ grouped })
Then in render() you only show the categories that have items like:
const menuBarButtons = Object.keys(this.state.grouped).map((category) => {
/* your jsx here */
return <MenuItem text={category} key={category} onClick={this.onClick} blah={blah}/>
/* or something , it's up to you */
})
I'm assuming you're showing the items based on the currently selected category this.state.selected. So after you have rendered your menu, you would do something like:
const selectedCatItems = this.state.grouped[this.state.selected].map((item) => {
return <YourItem name={item.name} key={item.id} blah={blah} />
})
Then render it:
return (
<div className="app">
<MenuBar blah={blah}>
{menuBarButtons}
</Menubar>
<div for your item showing area>
{selectedCatItems}
</div>
</div>
)
Also, don't forget to change your onClick() so that it sets this.state.selected state properly. I believe you can figure that out yourself.
Hope it helps.
PS: I didn't write a whole copy/paste solution to your problem simply because I'm reluctant to read and understand your UI details and the whole component to component data passing details..