Vue axios fetch data from api with - - api

I have this working code.
<template>
<v-card-text class="text-center">{{ ethPrice.ethereum.usd }} US$</v-card-text>
</template>
data() {
return {
ethPrice: { "ethereum": { "usd": "" } },
};
},
mounted() {
axios
.get(
"https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd"
)
.then((response) => (this.ethPrice = response.data));
But, when I do the same with this other I get this errors.
<template>
<v-card-text >{{ slpPrice.smooth-love-potion.usd }}</v-card-text>
</template>
data() {
return {
slpPrice: { "smooth-love-potion": { "usd": "" } },
};
},
mounted() {
axios
.get(
"https://api.coingecko.com/api/v3/simple/price?ids=smooth-love-potion&vs_currencies=usd"
)
.then((response) => (this.slpPrice = response.data));
Why one is working and the other one no? How can I fix this?
I tried to make it reactive object, but then the first is not working.
.then((response) => (this.$set(this.ethPrice, response.data)));

As you can see above comment, you have to access it like this.
<template>
<v-card-text >{{ slpPrice['smooth-love-potion'].usd }}</v-card-text>
</template>
...

Related

Losing my data when i refresh page in vuejs

I'm creating a social network for project in my formation, i have a like system and it work.
i have a components cardArticle with all info and i try to do a like count. It work but when i refresh the page or going on other page, i lost all my data likes (my data is not saved)
components/CardArticle.vue
<template>
<div id="card">
<div>
<a class="cardLink">
<img class="card-img" v-if="post.imageUrl !== undefined" :src="post.imageUrl" />
<h2 class="cardTitle"> {{ post.title }}</h2>
<p class="cardDescription"> {{ post.description }}</p>
</a>
</div>
<div class="buttonIcon">
<div>
<button type="button" class="buttonDelete" id="buttonDelete" #click="deletePost"
v-if="this.post.userId === this.user.userId || this.user.isAdmin === true">Supprimer</button>
<button type="button" class="buttonEdit" id="buttonEdit" #click="modifyPost"
v-if="this.post.userId === this.user.userId || this.user.isAdmin === true">
Editer
</button>
</div>
<div class="divIconLike">
<div class="iconLike">
<a #click="sendLike">
<i class="fa-regular fa-thumbs-up"></i>
</a>
</div>
<div class="countLike">
<p> {{ likes }} </p>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
import router from "../router/index.js";
export default {
name: 'CardArticle',
data () {
return {
likes: 0
}
},
props: {
post: {
type: Object
}
},
computed: {
user() {
return this.$store.getters.user;
}
},
methods: {
sendLike() {
axios.post("http://localhost:3000/api/articles/" + this.post._id + "/like", {
userId: this.user.userId
}, {
headers: {
Authorization: "Bearer " + this.user.token
}
})
.then(response => this.likes = response.data.article.likes)
.catch(error => console.log(error))
}
}
}
</script>
views/home.vue
<template>
<div class="home" v-if="this.user.token !== null">
<CardArticle v-for="post in allPosts" v-bind:key="post.id" :post="post" />
</div>
</template>
<script>
import CardArticle from "../components/CardArticle.vue"
import axios from "axios";
export default {
name: 'HomeArticle',
data () {
return {
post: {
title: "",
description: "",
imageUrl: ""
},
allPosts: [],
}
},
computed: {
user() {
return this.$store.getters.user;
}
},
components: {
CardArticle,
},
mounted() {
axios.get("http://localhost:3000/api/articles", {
headers: {
Authorization: "Bearer " + this.user.token
}
})
.then(response => {
this.allPosts = response.data;
})
.catch(error => {
return error;
})
}
}
</script>
What i should do for not losing my data ?
I would not use vuex or localstorage for that if possible, you have idea ?
Thanks for your help
If you loading data from server, then refresh page, you always will be lose data, because browser loading page again from server, and application will load data again.
If you don't want use vuex (but why not?), you can write data to cookies (by setting cookie value), then load it on app startup (when page is loaded). But it's not best practice at all. You can use vue3-cookies lib (link).
By the way, better learn to use stores, most progressive, I think, is Pinia.
Check: https://pinia.vuejs.org/
i lost all my data likes (my data is not saved)
likes is belong to each articles and It should have been saved to your db and call API to retrieve it again on component mounting:
export default {
name: 'CardArticle',
data () {
return {
likes: 0 // It's not managed by component state
}
},
methods: {
sendLike() {
axios.post("http://localhost:3000/api/articles/" + this.post._id + "/like", {
userId: this.user.userId
}, {
headers: {
Authorization: "Bearer " + this.user.token
}
})
.then(
// invalidates, update allPosts props (emit to parent)
)
.catch(error => console.log(error))
}
}
}

NuxtJS component - component renders without data

I have a component that uses fetch to bring data via Axios and return data to render. I am using the store to set/get the data. The component renders with empty data and after debugging I see that data() method is called before fetch() method.
How to fix the problem and bring the data before the component is rendered
here is the component code:
<template>
<v-card
class="mx-auto"
max-width="500"
>
<v-sheet class="pa-4 primary lighten-2">
<v-text-field
v-model="search"
label="Search Company Directory"
dark
flat
solo-inverted
hide-details
clearable
clear-icon="mdi-close-circle-outline"
></v-text-field>
<v-checkbox
v-model="caseSensitive"
dark
hide-details
label="Case sensitive search"
></v-checkbox>
</v-sheet>
<v-card-text>
<v-treeview
:items="items"
:search="search"
:filter="filter"
:open.sync="open"
>
<template v-slot:prepend="{ item }">
<v-icon
v-if="item.children"
v-text="mdi-${item.id === 1 ? 'home-variant' : 'folder-network'}"
></v-icon>
</template>
</v-treeview>
</v-card-text>
</v-card>
</template>
<script>
export default {
async fetch(){
console.log("Hi from Fetch !!!")
let response = await this.$axios.get('/items/tasks', {baseURL: 'http://localhost:8055'});
let tasks = response.data.data;
debugger
this.$store.commit('SET_ASSIGNMENTS', tasks);
},
data () {
debugger
console.log("data assignments: ", this.$store.state.assignments);
return {
items: this.$store.state.assignments,
open: [1, 2],
search: null,
caseSensitive: false,
}
},
computed: {
filter () {
return this.caseSensitive
? (item, search, textKey) => item[textKey].indexOf(search) > -1
: undefined
},
}
}
</script>
For this I use vuex this way:
const appStore = {
state () {
return {
data: [],
}
},
getters: {
data(state) {
return state.data
},
},
mutations: {
SET_ASSIGNMENTS(state, payload) {
state.data = payload
},
},
actions: {
async getData({ commit }, {fromDate, toDate}) {
let response = await this.$axios.get('/items/tasks', {baseURL: 'http://localhost:8055'});
let tasks = response.data.data;
commit("SET_ASSIGNMENTS", tasks);
}
}
}
export default appStore
Component code is like this:
<template>
. . .
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: "MyComponent",
components: {
. . .
},
computed: {
...mapGetters({
data: 'data'
})
},
mounted(){
this.getData();
},
methods: {
getData() {
this.$store.dispatch('getData')
}
}
}
</script>
data is not reactive, you can create a computed property that returns your items
ex:reactiveItems() {return this.items}

Vue - make axios request upon parent event

I'm basically trying to figure out how to listen for an event and call the axios request and rerender the results.
I have the following parent component (A select drop down) that has a change event that calls "appSelected"
<template>
<v-app>
<v-container>
<v-select :items="results" item-text="name" item-value="id" v-model="selectedOption" #change="appSelected"
:results="results"
label="Choose Application"
dense
></v-select>
</v-container>
</v-app>
</template>
appSelected
appSelected() {
Event.$emit('selected', this.selectedOption);
}
In my child component,I have an an Axios request that I'm trying to figure out how to call and re render when choosing a different option in the drop down. I know I shouldn't use "async mounted" but not sure what to use instead.
<template>
<div id="app" v-if="appselected">
<div class="card-header">
<h5 class="card-title">Components</h5>
</div>
<v-app id="inspire">
<v-container fluid>
<v-layout row wrap>
<v-container>
<v-flex xs12 md12 class="greyBorder blue-grey lighten-5">
<div class="mr-4 ml-4 whiteback userGroupHeight">
<v-layout row wrap>
<v-flex v-for="result in results" :key="result.name" xs6>
<v-checkbox light color="success" :label="result.name" v-model="result.selected">
</v-checkbox>
</v-flex>
</v-layout>
</div>
</v-flex>
</v-container>
</v-layout>
</v-container>
</v-app>
</div>
</template>
<script>
import Vuetify from 'vuetify'
import axios from 'axios'
export default {
data () {
return {
results: [],
appselected: false
}
},
methods: {
checkLength(index) {
if (index < this.types.length - 1) {
index = index + 1;
return index;
}
},
},
async mounted() {
try {
const response = await axios.get('/componentpopulate', { params: { query: this.query, appid: 2 } })
this.results = response.data
} catch(err) {
console.log(err)
}
},
created() {
Event.$on('selected', (selectedOption) => {
this.selected = selectedOption;
this.appselected = true;
console.log('component List application id - ' + this.selected);
});
}
}
</script>
<style scoped>
.v-application {
height: 250px;
}
</style>
This is my created method is where I'm listening for the event
created() {
Event.$on('selected', (selectedOption) => {
this.selected = selectedOption;
this.appselected = true;
console.log('component List application id - ' + this.selected);
});
}
Here is where I moved to the event listener method
created() {
Event.$on('selected', (selectedOption) => {
this.selected = selectedOption;
this.appselected = true;
console.log('component List application id - ' + this.selected);
const response = axios.get('/componentpopulate', { params: { query: this.query, appid: this.selected } })
this.results = response.data
console.log(response);
});
}
Here's the answer. Add the following
.then((response) => {
this.results = response.data
console.log(response)
});
Like this
created() {
Event.$on('selected', (selectedOption) => {
this.selected = selectedOption;
this.appselected = true;
console.log('component List application id - ' + this.selected);
const response = axios.get('/componentpopulate', { params: { query: this.query, appid: this.selected } })
.then((response) => {
this.results = response.data
console.log(response)
});
});
}
Hope this helps some one.
You can try this. Use watch for your selectedOption and make your API calls in the parent component. However required parameters for API call should be in parent component with this way. Or in your store. After you can pass your results to your child component as props and display it there. Here is a small example:
Your Parent Component:
<template>
<div>
<!-- your select box -->
<Child :results="results" />
</div>
</template>
export default {
import Child from "./Child"
components: {
Child
},
data() {
return {
selectedOption : null,
results: []
}
},
watch: {
selectedOption: async function() {
await axios.get('http://jsonplaceholder.typicode.com/posts')
.then(response => {this.results = response.data})
}
}
}
And in child component:
<template>
<div>
<div v-for="item in results" :key="item.id">
{{item}}
</div>
</div>
</template>
<script>
export default {
props: ['results']
}
</script>

Vuetify autocomplete not showing suggestions with more than one search term

When I make a search with Vuetify <v-autocomplete> and my API, mytext and myvalue are correctly updated and displayed in the suggestions only if write a word like FOO, if I write a string like FOO BAR, then I get the correct result with console.log(response.data) in the API call method, but I get nothing in the suggestions of <v-autocomplete>.
<template>:
<v-autocomplete
v-model="select"
:loading="loading"
:items="items"
item-text="mytext"
item-value="myvalue"
:search-input.sync="search"
hide-no-data
hide-details
label="My Autocomplete"
>
<template v-slot:item="data">
<v-list-item-content>
<v-list-item-title v-html="data.item.mytext"></v-list-item-title>
<v-list-item-subtitle
v-html="data.item.myvalue"
></v-list-item-subtitle
></v-list-item-content>
</template>
</v-autocomplete>
<script>:
<script>
export default {
data() {
return {
select: null,
loading: false,
items: [],
search: null
}
},
watch: {
search(val) {
console.log('search: ' + val)
val && val !== this.select && this.query(val)
}
},
methods: {
async query(v) {
this.loading = true
await this.$axios
.get('/api/foo', {
params: {
search: this.search
}
})
.then((response) => {
console.log(response.data)
this.items = response.data
})
.catch((error) => {
console.log(error)
})
.finally(() => {
this.loading = false
})
}
}
}
</script>
The search variable seems to be linked to the items variable.
You can apply no-filter prop to your v-autocomplete component.
<v-autocomplete
...
no-filter
...
>
</v-autocomplete>
As written in documentation for this prop:
Do not apply filtering when searching. Useful when data is being
filtered server side
https://vuetifyjs.com/en/api/v-autocomplete/#props
I finally fixed it by adding this prop to <v-autocomplete>:
:filter="() => true"

Vuejs - Same component with different data

I am trying to re-use the same component, but load the components with different data. I thought simply providing unique id's would do the trick, but no luck. I switched from a Vuex store for this data, to using a dataShare This is what I'm doing:
The components:
<logoHeader :panel=0 title="Add your logo / header" id="top" topPadding="pt-10" />
<logoHeader :panel=1 title="Add your main image" id="main" topPadding="pt-0"/>
So its the exact same component, with some different props and different ids
This is the logoHeader component:
<template>
<v-row
:class="topPadding"
align="center"
justify="center"
>
<v-col
align="center"
justify="center"
cols="12"
>
<v-hover v-slot:default="{ hover }">
<v-card
:elevation="hover ? 12 : 0"
class="mx-auto"
max-width="600"
>
<v-img
v-if="showImage"
:src="imageUrl"
max-width="600px"
class="pointer"
#click="panel = 0"
>
</v-img>
<v-icon
v-if="!showImage"
class="my_dark_purple_text"
size="100"
#click="sendToPanel"
>add_box</v-icon>
<p class="my_dark_purple_text">{{ title }}</p>
<p>URL {{ imageUrl }}</p>
<p>Show image? {{ showImage }}</p>
</v-card>
</v-hover>
</v-col>
</v-row>
</template>
<script>
import {mapGetters} from 'vuex';
import {mapActions} from 'vuex';
import {dataShare} from '../../../packs/fresh-credit.js';
export default {
props: ['panel', 'title', 'topPadding'],
data() {
return {
imageUrl: "",
showImage: false,
}
},
created() {
dataShare.$on('imageUrl', (data) => {
this.imageUrl = data;
});
dataShare.$on('showImage', (data) => {
this.showImage = data;
});
},
computed: {
...mapGetters('emailPanel', [
'returnPanel'
]),
},
methods: {
...mapActions('emailPanel', [
'updatePanel'
]),
sendToPanel() {
this.updatePanel(this.panel);
},
},
}
</script>
And then finally this is where the data enters the system:
<template>
<v-expansion-panel-content>
<h1 class="subtitle-1 font-weight-bold">Only images files accepted</h1>
<v-file-input
v-model="image"
accept="image/*"
label="Image Upload"
prepend-icon="add_a_photo"
color='#68007d'
></v-file-input>
<v-btn
:disabled="disableUpload"
color="#68007d"
class="white--text"
#click="sendImage"
>Submit</v-btn>
</v-expansion-panel-content>
</template>
<script>
import axios from 'axios';
import {dataShare} from '../../../../packs/fresh-credit.js';
export default {
data() {
return {
image: null,
disableUpload: true,
}
},
watch: {
image: function() {
if(this.image.size > 0){
this.disableUpload = false;
}
else{
this.disableUpload = true;
}
}
},
computed: {
},
methods: {
sendImage() {
let formData = new FormData();
formData.append('file', this.image);
axios.post('/admin/features/images', formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(response => {
dataShare.$emit('imageUrl', response.data);
dataShare.$emit('showImage', true);
});
}
},
}
</script>
Where did I go astray?
Add the key property to the components and set it to different values (for example 1 and 2). If the key has different values Vue will differentiate them when rendering. Here is a basic explanation.