Vue.js return data from axios - vue.js

<template>
<div class="comment">
<div v-for="(comment, index) in comments" :key="index">
{{ getUser(comment.student_idx) }}
</div>
</div>
</template>
<script>
import axios from 'axios'
import server from '#/models/server'
export default {
data() {
return {
url: server,
comments: {}
}
},
props: ['idx'],
created() {
axios.get(`${this.url}/bamboo/reply?post=${this.idx}`)
.then(response => {
if (response.data.status === 200) {
this.comments = response.data.data.replies
}
})
},
methods: {
getUser (idx) {
axios.get(`${this.url}/member/student/${idx}`)
.then(response => {
if (response.data.status === 200) {
return response.data.data.member.name
}
})
}
}
}
</script>
I would like to load the comments at created and print them out using v-for.
In v-for, I would like to load the member.name from each comment
But {{ getUser(comment.student_idx) }} is undefined.
I don't know how to return data from axios
Help me please!!

Your method should not be async for stable run code. My recomendation is next code:
<template>
<div class="comment">
<div v-for="(comment, index) in comments" :key="index">
{{ comments['user'] }}
</div>
</div>
</template>
<script>
import axios from 'axios'
import server from '#/models/server'
export default {
data() {
return {
url: server,
comments: []
}
},
props: ['idx'],
created() {
axios.get(`${this.url}/bamboo/reply?post=${this.idx}`)
.then(response => {
if (response.data.status === 200) {
this.comments = response.data.data.replies;
if(this.comments)
for(let comment of this.comments){
this.getUser(comment, comment.student_idx);
}
}
})
},
methods: {
getUser (comment, idx) {
axios.get(`${this.url}/member/student/${idx}`)
.then(response => {
if (response.data.status === 200) {
this.$set(comment, 'user', response.data.data.member.name);
}
})
}
}
}
</script>

Related

Vue.js Composition API vs Options API - posts ref in a v-for loop

I'm still new to VueJS, and I cannot figure this one out.
I have a page that loads with axios from wordpress my posts:
export default {
data() {
return {
postsUrl: "https://localhost/wordpress/wp-json/wp/v2/news",
posts: [] as any[],
isLoading: false,
regex: /(<([^>]+)>)/gi,
errorCaught: false,
};
},
methods: {
getPosts() {
this.isLoading = true;
axios
.get(this.postsUrl, { params: { _embed: true } })
.then((response) => {
this.posts = response.data;
console.log("Pages retrieved!");
console.log(this.posts);
this.isLoading = false;
})
.catch((error) => {
console.log(error);
if (error) {
this.isLoading = false;
setTimeout(() => {
this.errorCaught = true;
}, 1100);
}
});
},
},
mounted() {
this.getPosts();
},
};
and the template
<template>
<div class="news-container">
<div class="loading">
<transition name="fadeLoading">
<div v-if="isLoading">Loading...</div>
</transition>
<transition name="fadeLoading">
<div v-if="errorCaught">There was an error loading news</div>
</transition>
</div>
<ul v-for="(index) in posts" :key="index" ref="posts">
<h1>
<router-link :to="index.slug" tag="div" key="page.id"
>{{ index.title.rendered }}
</router-link>
</h1>
<img
v-if="index._embedded['wp:featuredmedia']"
:src="index._embedded['wp:featuredmedia'][0].source_url"
/>
<p class="date">{{ index.date }}</p>
</ul>
</div>
</template>
This works fine, no problem with the Options API.
But when I want to convert this to the Composition API, the posts don't appear:
<script setup lang="ts">
import axios from "axios";
import { ref } from "vue";
import { onMounted } from "vue";
const postsUrl = "https://localhost/wordpress/wp-json/wp/v2/news";
var posts = ref([] as any);
const isLoading = ref(false);
const regex = /(<([^>]+)>)/gi;
const errorCaught = ref(false);
const getPosts = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: { _embed: true, page: 1 } })
.then((response) => {
posts = response.data;
console.log("Pages retrieved!");
console.log(posts);
isLoading.value = false;
})
.catch((error) => {
console.log(error);
if (error) {
errorCaught.value = true;
}
});
};
onMounted(() => {
getPosts();
});
</script>
I suspect there is a misunderstanding on my part how refs work, because If I edit the <template> (and have npm run dev running the server), I can see that the posts appear.
Could you help me with what am I doing wrong and why?
Thank you for your time
Your mistake is here
posts = response.data;
When you use ref you have to use it with value. Change your code to this:
posts.value = response.data;
It should be working fine. For more detail you should check here:
https://vuejs.org/api/reactivity-core.html#ref

How to set up Pinia getter in Vue 3 Composition API

I am building a Pokemon filtered search app using Vue 3, Composition API, and Pinia. I am attempting to set up the app so that the fetched response from the Pokemon API is passed to a store (set up using Pinia) inside the fetchPokemon() function.
const fetchPokemon = () => {
axios.get("https://pokeapi.co/api/v2/pokemon?offset=0")
.then((response) => {
store.addPokemon(response.data.results)
})
}
After passing the response to the store, the updatePokemon() function uses filter and include methods to filter out and match Pokemon in the store with Pokemon in the user-input text field ("state.text"):
const updatePokemon = () => {
if(!state.text) {
return []
}
return store.getState.pokemons.filter((pokemon) =>
pokemon.name.includes(state.text)
)
}
When executing the app, I am getting the following error in the updatePokemon() function:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'includes')
I'm assuming this means the .includes() method for searching/filter cannot be used for this search. How should I go about handling the filter and include methods to match Pokemon in the store with the user-inputted Pokemon?
Here is the code:
Pinia Store
import { defineStore } from 'pinia'
export const usePokemonStore = defineStore({
id: 'store',
state: () => ({
pokemons: []
}),
getters: {
getState(state) {
return state
}
},
actions: {
addPokemon(name) {
this.pokemons.push(name)
}
}
})
Component
<template>
<div class="w-full flex justify-center">
<input type="text" placeholder="Enter Pokemon here"
class="mt-10 p-2 border-blue-500 border-2" v-model="text"/>
</div>
<div class="mt-10 p-4 flex flex-wrap justify-center">
<div class="ml-4 text-2x text-blue-400"
v-for="(pokemon, idx) in filteredPokemon" :key="idx">
<router-link :to="`/about/${getPokemonId(pokemon.name)}`">
{{ pokemon.name }} - with id {{ getPokemonId(pokemon.name) }}
</router-link>
</div>
</div>
</template>
<script>
import axios from 'axios';
import { reactive, toRefs, computed } from 'vue';
import { usePokemonStore } from '#/store';
export default {
name: 'Home',
setup() {
const store = usePokemonStore()
const state = reactive({
text: "",
filteredPokemon: computed(()=> updatePokemon())
})
const updatePokemon = () => {
if(!state.text) {
return []
}
return store.getState.pokemons.filter((pokemon) =>
pokemon.name.includes(state.text)
)
}
const fetchPokemon = () => {
axios.get("https://pokeapi.co/api/v2/pokemon?offset=0")
.then((response) => {
store.addPokemon(response.data.results)
})
}
fetchPokemon()
const getPokemonId = (item) => {
console.log(item)
return store.pokemons.findIndex((p) => p.name === item) + 1
}
return { ...toRefs(state), fetchPokemon, getPokemonId, updatePokemon, store }
}
}
</script>
UPDATED
Store - with not action
import { defineStore } from 'pinia'
export const usePokemonStore = defineStore({
id: 'store',
state: () => ({
pokemons: []
})
})
Component - with no store.addPokemon(...)
<template>
<div class="w-full flex justify-center">
<input type="text" placeholder="Enter Pokemon here"
class="mt-10 p-2 border-blue-500 border-2" v-model="text"/>
</div>
<div class="mt-10 p-4 flex flex-wrap justify-center">
<div class="ml-4 text-2x text-blue-400"
v-for="(pokemon, idx) in filteredPokemon" :key="idx">
<router-link :to="`/about/${getPokemonId(pokemon.name)}`">
{{ pokemon.name }} - with id {{ getPokemonId(pokemon.name) }}
</router-link>
</div>
</div>
</template>
<script>
import axios from 'axios';
import { reactive, toRefs, computed } from 'vue';
import { usePokemonStore } from '#/store';
export default {
name: 'Home',
setup() {
const store = usePokemonStore()
const state = reactive({
// pokemons: [],
text: "",
filteredPokemon: computed(()=> updatePokemon())
})
const updatePokemon = () => {
if(!state.text) {
return []
}
return store.pokemons.filter((pokemon) =>
pokemon.name.includes(state.text)
)
}
const fetchPokemon = () => {
axios.get("https://pokeapi.co/api/v2/pokemon?offset=0")
.then((response) => {
store.pokemons = response.data.results
})
}
fetchPokemon()
const getPokemonId = (item) => {
console.log(item)
return store.pokemons.findIndex((p) => p.name === item) + 1
}
return { ...toRefs(state), fetchPokemon, getPokemonId, store }
}
}
</script>
First of all, you don't need getState at all.
You can use usePokemonStore().pokemons directly. The object returned by calling usePokemonStore() function includes:
all state properties
all actions
all getters.
Here's how to get the filtered pokemon array, based on whether their name includes state.text:
setup() {
const store = usePokemonStore();
const state = reactive({
text: "",
filteredPokemons: computed(() => store.pokemons.filter(
pokemon => pokemon.name.includes(state.text)
))
});
return {
...toRefs(state)
}
}
Working example:
const { createApp, reactive, toRefs, computed, onMounted } = Vue;
const { defineStore, createPinia } = Pinia;
const usePokemons = defineStore('pokemon', {
state: () => ({ pokemons: [] })
});
const pinia = createPinia();
createApp({
pinia,
setup() {
const store = usePokemons(pinia);
const state = reactive({
searchTerm: '',
filteredPokemons: computed(() => store.pokemons.filter(
pokemon => pokemon.name.includes(state.searchTerm)
))
});
onMounted(() => {
fetch('https://pokeapi.co/api/v2/pokemon?offset=0')
.then(r => r.json())
.then(r => store.pokemons = r.results)
});
return {
...toRefs(state)
}
}
}).mount('#app')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/vue-demi"></script>
<script src="https://unpkg.com/pinia#2.0.11/dist/pinia.iife.prod.js"></script>
<div id="app">
<input v-model="searchTerm">
<div v-for="pokemon in filteredPokemons">
{{ pokemon.name }}
</div>
</div>

Vue with Axios drop down pre populate

I'm trying to figure out how to pre-populate my vue drop down.
When I click on the drop down it doesn't show anything, but when I click off of it and back on it, it will show the options. This is not the behavior I want. I would like it to show the options when clicking on it once.
<template>
<select v-model='selectedApp' #click="populateApp(results)">
<option value="" disabled> Select an application</option>
<option v-for="result in results">{{ result.name }} {{result.id}}</option>
</select>
</template>
<script>
import axios from 'axios'
export default{
data(){
return {
selectedApp: '',
results: []
}
},
methods: {
populateApp: function(event){
axios.get('/apdata',{params: {query: this.query}})
.then(response => {
this.results = response.data;
console.log(this.results);
});
}
}
}
</script>
Thanks for you help!
You can do something like this. And you forgot to bind the value to option. Here's sample
<template>
<div>
<select v-model="selectedApp">
<option selected disabled value="">Choose</option>
<option v-for="result in results" :value="result.id">{{ result.name }} {{ result.id }}</option>
</select>
<div>selectedApp: {{ selectedApp }}</div>
</div>
</template>
export default {
data() {
return {
selectedApp: "",
results: []
}
},
async mounted() {
try {
const response = await axios.get('/apdata', { params: { query: this.query } })
this.results = response.data
} catch(err) {
console.log(err)
}
}
}
Here is you answer using #Naren suggested
import axios from 'axios'
export default {
data() {
return {
selectedApp: '',
results: []
}
},
mounted() {
axios.get('/apdata', {
params: {
query: this.query
}
})
.then(response => {
this.results = response.data;
console.log(this.results);
})
},
props:{},
....
}
If you are using nuxt i suggest using AsyncData as it will automatically bind the data to the property to be used
async asyncData({ app, params }) {
const { data } = await app.$axios.get(`path/${params.code}`)
return { results: data.error ? [] : data }
//data(){ results:[] } does not need to be defined as this will be taken care of by asyncData
}
If you are using vuejs3 you will need to do the same thing as mounted hook except in setup() additionally you may need to import the router
import { useRoute } from 'vue-router'
export default {
setup(props, ctx) {
const route = useRoute()
onMounted(() => {
const id = route.params.id
///add code here
})
}
}

Call $emit after dispatch action in Vuejs

I have a parent component:
<template>
<div class="w-full">
<div class="card-body">
<city-edit-form :form="form" :resource="resource" #save_resource="func">
</city-edit-form>
</div>
</div>
</template>
<script>
export default {
methods: {
func() {
console.log("test");
}
}
};
</script>
And child component:
<template>
<div>
<form action="#" method="POST" #submit.prevent="submit" v-else>
<button type="submit" class="btn-green">Save</button>
</form>
</div>
</template>
<script>
import { UPDATE_RESOURCE } from "../../../Stores/action-types";
export default {
props: {
form: { required: true },
resource: { required: true }
},
methods: {
submit() {
this.$store
.dispatch(`city/${UPDATE_RESOURCE}`, this.form)
.then(() => this.$emit("save_resource"));
}
}
};
</script>
And action is:
[UPDATE_RESOURCE]({ commit, state }, form) {
commit(SET_LOADING, true);
return ResourceService.update(state.resource.id, form)
.then(({ data }) => {
commit(SET_RESOURCE, data);
})
.catch(errors => {
commit(SET_ERRORS, errors.response.data);
})
.finally(() => commit(SET_LOADING, false));
});
},
When I submit form, action has been dispatched, but nothing emitted.
Nothing logged in console. Where I make mistake?
update
When I check Vue toolbar's Event tab, I see this:
I think event has been emmitted, but console.log logs nothing in console! So wired!
Use return keyword while resolve or reject is triggered
[UPDATE_RESOURCE]({ commit, state }, form) {
commit(SET_LOADING, true);
return new Promise((resolve, reject) => {
ResourceService.update(state.resource.id, form)
.then(({ data }) => {
commit(SET_RESOURCE, data);
return resolve();
})
.catch(errors => {
commit(SET_ERRORS, errors.response.data);
return reject();
})
.finally(() => commit(SET_LOADING, false));
});
},
instead of emitting events (nothing wrong with that) you could use mapGetters
<template>
<div class="w-full">
<div class="card-body">
<city-edit-form :form="form" :resource="myResource">
</city-edit-form>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters({
myResource: 'Stores/action-types/SET_RESOURCE', <---- needs to be modified
}),
}
};
</script>
this is assuming you have made any getters

Can't display data in a component

I have request that returns json data...and i'm trying to display that data in vue component,but it doesn't work. In console.log is everything ok..json is something like:
[{"id":1,"body":"Hello, this is my first notification","..bla bla
here is my code
<template>
<div class="notification-container">
<p>{{ notification }}</p>
</div>
</template>
<script>
export default {
data() {
return {
notification: '',
}
},
mounted() {
axios.get('/notifications').then((response) => {
this.notification = response.data[0].body;
console.log(this.notification);
});
}
}
</script>
Try something like this :
<template>
<div class="notification-container">
<p>{{ notification /* OR this.notification[0].body */ }}</p>
</div>
</template>
<script>
export default {
data() {
return {
notification: '',
}
},
methods:{
showMsg(){
axios.get('/notifications').then( response => {
console.log(response);
this.notification = response.data[0].body;
/* OR this.notification = response; */
console.log(this.notification);
}).catch(e => {
console.log(e)
});
}
},
created() { // or mounted()
this.showMsg();
}
}
</script>