Why v-for & axios not loading items one by one? - javascript

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.

Related

Generate multiple select inputs with v-for

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 :)

Vue-js call object with special id

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.

React mapping menu from JSON

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..

How to pass query parameter to api using map method and concatenate every json response to setState in react?

In below code i am calling api by passing query parameter by map method and i want to concatenate the resulted json of each query and update the state.
But here i am facing problem that setState is updated with the any one of returned json.
import React from 'react';
const api_key = "key";
class card extends React.Component {
state = {
articles: [],
parameter: ['a', 'b', 'c', 'd', 'e']
};
componentDidMount = async () => {
this.state.newsPaper.map(async (querypara) => {
const requstone = await fetch(`https:someapisources=${querypara}&apiKey=${api_key}`);
const dataone = await requstone.json();
this.setState({
articles: dataone.articles
});
});
}
render() {
return (
<div>
<div>
{this.state.articles.map((article, index) => {
return (
<div key={index}>
<div>
<img src={article.urlToImage} alt="Avatar" />
<div>
<h4><b>{article.title}</b></h4>
<p>
{article.description}
</p>
<section>
<div>
<span>Source:</span>
<span>{article.source.name}</span>
</div>
</section>
</div>
</div>
</div>
)
})}
</div>
</div>
);
}
}
export default card;
I want to add each response json and render it by setting the state how can i do that
Below is the sample json response.
{
"status": "ok",
"totalResults": 8,
"articles": [{
"source": {
"id": "recode",
"name": "Recode"
},
"author": "Recode Staff",
"title": "title",
"description": "Something",
"url": "url",
"urlToImage": "image url.jpg",
"publishedAt": "2018-11-26T13:23:06Z",
"content": "some content"
},
{
"source": {
"id": "recode",
"name": "Recode"
},
"author": "Recode Staff",
"title": "title",
"description": "Something",
"url": "url",
"urlToImage": "image url.jpg",
"publishedAt": "2018-11-26T13:23:06Z",
"content": "some content"
}
]
}
If I understand your question correctly, then the only adjustment needed here is to update your setState() logic so that you append articles from your response to the articles array in your state:
this.state.newsPaper.map(async (querypara) => {
const requstone = await fetch(`https:someapisources=${querypara}&apiKey=${api_key}`);
const dataone = await requstone.json();
// Concatenate articles from response (dataone) to array in state
this.setState({
articles: this.state.articles.concat(dataone.articles)
});
}

How get extra information from json?

I use can.Component to dispay JSON on the page.
can.Component.extend({
tag: "some-app",
scope: {
items: new Items.List({}),
displayedItems: function () {
...
return items;
}
},
helpers: {
...
},
events: {
"{Items} created": function (Items, ev, newItem) {
...
}
}
})
How can I get "meta" section of received JSON (below) to the scope or helpers?
{
"data": [
{
"description": "Some text",
"id": 1,
"measurement": "pcs",
"name": "Name of item",
"resource_uri": "/api/v1/item/1/"
},
{....}, {....}
}
],
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 3
}
}
I can get it in console with Items.findAll().then(function(info){console.log(info.attr('meta'))}) , but I'm noob in (can.)js and can't understand how to get it in the place I need.
Instead of this:
scope: {
items: new Items.List({})
}
make the request:
scope: {
items: Items.findAll()
}
There are other ways to do this as well, in the template(not advised), or creating the request in another controller or component and passing in to the instantiation of the component.
If you want more specifics, you would nee to update your question with more details on your model.

Categories