Dynamic list not rendered - vue.js

I'm a beginner in VueJS so any help would be appreciated.
So I'm trying to display a list from my API. My API is working fine as I have tested it in Postman but when I try to create a list based on the response of my API, the list is not displayed. What am I doing wrong?
Here is my html:
<div id="tabs">
<ul>
<li v-for="user in users">
{{ user.userid }}
</li>
</ul>
</div>
And here is my js:
var tabs = new Vue({
el: '#tabs',
data: {
users: []
},
mounted: function(){
this.getAllUsers()
},
methods: {
getAllUsers: function(){
axios.get('<?php echo base_url(); ?>users/all_users')
.then(function(response){
this.users = response.data
console.log(this.users)
})
}
}
})
Here is the screenshot of the console, the data are just for testing.
value of users after getting response from API

In your axios "then" method, you write:
.then(function(response){
this.users = response.data;
});
When using function keyword to declare a function, it creates its own context and therefore this value of its parent scope is not passed down to the function.
In Vue's case, this in the child function is not the intended Vue instance.
To solve this, simply pass the parent's this value to the callback, or use arrow functions.
Passing down using .bind
.then(function(response){
this.users = response.data;
}.bind(this));
Using arrow function syntax
.then(response => {
this.users = response.data;
});

<?php echo base_url(); ?>
I don't think this will be rendered in a js file. try hardcoding your baseURL instead.
Example:
getAllUsers: function(){
axios.get('http://app.test/users/all_users') // or axios.get('/api/users/all_users') if you're using an api based route for your api
.then(function(response) {
this.users = response.data
console.log(this.users)
})
}

Related

Vue 3 display fetch data v-for

So, I'm creating a Pokemon application and I would like to display the pokemon names using the api : https://pokeapi.co/api/v2/pokemon/.
I'm doing a fetch request on the api and then display the pokemon names in my template. I have 0 problem when I try to display only 1 pokemon but I have this error when I try to display all my pokemons using v-for.
Do you have any idea why I meet this error ?
<template>
<p class="dark:text-white"> {{pokemons[0].name}} </p> //working
<div v-for="(pokemon, index) in pokemons" :key="'poke'+index"> //not working...
{{ pokemon.name }}
</div>
</template>
<script>
const apiURL = "https://pokeapi.co/api/v2/pokemon/"
export default {
data(){
return{
nextURL:"",
pokemons: [],
};
},
created(){
this.fetchPokemons();
},
methods:{
fetchPokemons(){
fetch(apiURL)
.then( (resp) => {
if(resp.status === 200){
return resp.json();
}
})
.then( (data) => {
console.log(data.results)
// data.results.forEach(pokemon => {
// this.pokemons.push(pokemon)
// });
// this.nextURL = data.next;
this.pokemons = data.results;
console.log(this.pokemons);
})
.catch( (error) => {
console.log(error);
})
}
}
}
</script>
<style>
</style>
I've just pasted your code into a Code Pen and removed the working/not working comments and the code runs and shows the names.
Maybe the problem is in the parent component where this component is mounted, or the assignment of the :key attribute
try :key="'poke'+index.toString()", but I'm pretty sure js handels string integer concats quiet well.
Which version of vuejs do you use?
Edit from comments:
The parent component with the name PokemonListVue imported the posted component as PokemonListVue which resulted in a naming conflict. Renaming either one of those solves the issue.
In the error message posted, in line 3 it says at formatComponentName this is a good hint.

How to update a variable from an asynchronous function?

In my template, in a v-slot (which means users is not available in <script setup>), I have
<template v-slot:body-cell-assignedTo="props">
<q-td :props="props">
<div v-for="u in props.users" :key="u">{{u}}</div>
</q-td>
</template>
This displays
john
mary
I can enrich this information by calling an API:
fetch('https://example.com/api/user/john')
.then(r => r.json())
.then(r => console.log(r))
This displays in the console John de Brown, 1262-1423.
My question: how to combine these two mechanisms? In other words, how to asynchronously update the value in {{}}?
I would need to do something like
<div v-for="u in props.users" :key="u">{{enrichFetchFunction(u)}}</div>
but it would need to be asynchronous, and yet somehow return a value.
EDIT: I will ultimately enrich the source data that is displayed in the v-slot. I would still be interested, though, if waiting for such an asynchronous function there (à la await) is doable in Vuie.
I assume you are using Compositions API. See this playground
<script setup>
import { ref, onMounted } from 'vue'
const users = ref([])
onMounted(async() => {
fetch('https://mocki.io/v1/67abcfb6-4f25-4513-b0f9-1eb6c4906413')
.then(r => r.json())
.then(r => users.value = r)
})
</script>
<template>
<div v-for="u in users" :key="u">{{u}}</div>
</template>
This is doable with Lifecycle hooks such as mounted(), yet you will need some sort of listener to react to the information being changed. here is an example that updates the values as soon as it is mounted and includes a button that will also update the values (you can run the code here in Vue SFC Playground):
<template>
<div id="app">
<h1 v-for="u in enrichedUsers" :key="u">{{ u }}</h1>
<button #click="myAsyncFunction">
update
</button>
</div>
</template>
<script>
// pseudo api
const fetchEnrichedAPI = function(user) {
return new Promise( (resolve, reject) => {
var enrichedUsers = []
if (user.includes('john')) {
enrichedUsers.push('John de Brown, 1262-1423')
}
if (user.includes('mary')){
enrichedUsers.push('Mary de Purple, 1423-1262')
}
setTimeout(() => {
resolve(enrichedUsers);
}, 300);
});
}
export default{
data() {
return {
props : { users: ['john','mary'] },
enrichedUsers: []
}
},
mounted() {
// when mounted run this async function
this.myAsyncFunction()
},
methods: {
async myAsyncFunction() {
// call api passing the list of users
await fetchEnrichedAPI(this.props.users)
.then((data) => {
// if api work
this.enrichedUsers = data;
return true;
})
.catch((e) => {
// if the api doesn't work
console.error(e);
this.enrichedUsers = this.props.users;
})
}
},
}
</script>
I am aware that this does not use props, but it does work. If you would like to expand this to use props you may be able to do this with computed properties or functions in the v-for. See this post for more info on that.

Fetch each object from API data using vue and axios

This is my script that calls axios and fetch data as posts
<script>
import axios from 'axios'
export default {
name: 'App',
mounted: function () {
axios.get('API URL')
.then(response => this.posts = response.data)
},
data() {
return {
posts: null
}
},
};
</script>
My code on view that tries to fetch data as posts from the script above
<template>
<div id="app">
<ul>
<li v-for="post in posts" v-text="post.createdAt"></li>
</ul>
<div>
</template>
SAMPLE data fetched from API URL look like this
POSTS OBJECT VARIABLES
I am able to fetch API DATA in console log as an array but when I call one object from array which is createdAT, v-text = "post.createdAt" does not print/fetch list of createdAt date list.
Just solved it following this document USING AXIOS TO CONSUME API here is the link for that https://v2.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html .
Above code that I have posted works fine. Problem was on my API URL which was nested inside data[data[object]]. So the way I called data from that API
from this
mounted: function () {
axios.get('API URL')
.then(response => this.posts = response.data)
}
to this
mounted: function () {
axios.get('API URL')
.then(response => this.posts = response.data.data)
}
posts isn't reactive because the default value is null, make it an empty array, or use Vue.set instead:
Array:
posts: []
Vue.set:
.then(response => Vue.set(this, 'posts', response.data))
Edit
In response to the comments below:
You must import Vue from 'vue' to resolve the Vue is not defined error.
Regarding your v-for-key needing to be unique, you need to define a unique key on v-for elements. You can just use JSON.stringify(post):
<li v-for="post in posts" v-text="post.createdAt" :key="JSON.stringify(post)"></li>

Dynamic v-for rendering from http get results to Vue component

I'm trying to achieve a dynamic rendering of elements base from the return items array of the target API endpoint. Below is what I've tried
<template id="list-comp-tpl">
<button #click="searchNow">Search Now</button>
<ul v-for="item in searchResults">
<li>{{ item.name }}</li>
</ul>
</template>
<div id="app">
<list-comp></list-comp>
</div>
Vue.components('list-comp',{
template : '#list-comp-tpl',
data() {
return {
searchResults : [];
}
},
method : {
searchNow(){
// perform api query
axios.get('http://apiwebsite.com/search')
.then(function (response) {
// handle success
this.searchResults = response.data.msg;
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
}
}
});
new Vue({
el '#app'
});
But the component list-comp is not updating at all like when there's a return data from the api, it does not display as I expect it to. What can I try next?
for me "this" is not visible within function(response)
solutions that work for me
.then(response => this.searchResults = response.data.msg)
or, If you are keen on function(response) try this:
searchNow(){
_self = this;
// perform api query
axios.get('http://apiwebsite.com/search')
.then(function (response) {
// handle success
_self.searchResults = response.data.msg;
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
}
Try assigning values as shown.
this.searchResults = Object.assign({}, response.data.msg);
The way you are setting the values in array they are not set as reactive property on the DOM. Hence your component is not updated with new data.
You can also use Vue.set for updating array values.
Refer below link.
https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats

VueJS + Rest API list rendering issue

I have a Spring Data Rest backend and in src/main/resources/static html + js assets which work fine. My issue is that I can't understand how to render the data picked up from the webservice in the interface.
In case I set the data explicitly as an array, it works fine (see https://v2.vuejs.org/v2/guide/list.html).
Thank you in advance!
...
const ListFormsApi = {
template: '<div><ul><li v-for=\'item in items\'>{{item.details.Title}}</li></ul></div>',
data: function(){
return {
items: ''
}
},
created: function() {
this.get()
},
methods: {
get: function() {
axiosInstance.get('/contactTemplate')
.then(function (response) {
this.items = response.data._embedded.contactTemplate
}).catch(function (error) {
console.log(error)
}
);
}
}
}
...
The webpage is quite simple and straightforward from documentation examples (assume that complete html and head tags are present as well...
<body>
<div id="app">
<h1><router-link to="/">ContactForm Factory!</router-link></h1>
<p>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
<router-link to="/listForms">List Forms</router-link>
<router-link to="/listFormsApi">List Forms API</router-link>
</p>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router#2.0.0/dist/vue-router.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="app.js"></script>
</body>
I think this is happening because scope of this is not what you are expecting inside the then block of axiosInstance, you need to make following change to make it work.
methods: {
get: function() {
var self = this
axiosInstance.get('/contactTemplate')
.then(function (response) {
self.items = response.data._embedded.contactTemplate
}).catch(function (error) {
console.log(error)
}
);
}
You can have a look at my answer on the similar problem here.
When you are registering the .then() callback the context of the function changes.
In order to keep the context you can use the bind method.
methods: {
get: function() {
axiosInstance.get('/contactTemplate')
.then(function (response) {
this.items = response.data._embedded.contactTemplate
}.bind(this)) // added .bind
.catch(function (error) {
console.log(error)
});
}
}
vue-resource is now using response.body rather than data so look to update as follows:
methods: {
get: function() {
axiosInstance
.get('/contactTemplate')
.then((response) => {
this.$set(this.$data, 'items', response.body._embedded.contactTemplate)
})
.catch((error) => {
console.log(error)
});
}
}
I've also used arrow syntax to correct the scope of this and this.$set to ensure the data that you set is reactive.
If this still doesn't produce the desired result I'd confirm the data is correctly returning from the endpoint. Vue-resource has methods such as response.json() available if for instance the server responds with an incorrect content-type.