How to loop through an API endpoint JSON Object in Vue & Axios? - javascript

I have an API endpoint which is a JSON object. I am using Axios and Vuejs to fetch the data into the DOM, but I am only able to get the whole object. When I tried to loop throught with the v-for directive it doesn't output the specific item in the object.
I fetched the data using Axios like so:
export default {
name: 'Reviews',
props: {
title: String
},
data(){
return {
info: []
}
},
// Life cycle hook that calls axios
mounted(){
axios.get('http://dev.muvtravel.com/index.php/explore/test?tripid=6590').then(response => {
console.log(response.data)
this.info = response.data
})
}
}
Then tried loop through using v-for
<div v-for="(item, index) in info" :key="index">
{{ item.establishment_address }}
{{ item.phone }}
</div>
<template>
<div class="reviews container-fluid">
<h1 class="text-center">{{ title }}</h1>
<b-container>
<b-row>
<b-col cols="12" sm="12" md="12" lg="4" xl="4">
Column 1
</b-col>
<b-col cols="12" sm="12" md="12" lg="8" xl="8">
Column 2
</b-col>
</b-row>
</b-container>
<div v-for="(item, index) in info" :key="index">
{{ item.establishment_address }}
{{ item.phone }}
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Reviews',
props: {
title: String
},
data(){
return {
info: []
}
},
// Life cycle hook that calls axios
mounted(){
axios.get('http://dev.muvtravel.com/index.php/explore/test?tripid=6590').then(response => {
console.log(response.data)
this.info = response.data
})
}
}
</script>
<style scoped lang="scss">
</style>
Any help will be appreciate it

So I checked to see if the API endpoint in your code was publicly open - it is.
From looking at your payload, the reason your code isn't working is because you're trying to iterate on an object. The data object that you're returning is the FULL payload from that API endpoint, which is an object {"success": true, "data": [...]"}.
To more clearly illustrate what I'm talking about, here's an example fetch you can run:
fetch(yourAPIEndpoint).then(res => res.json()).then(data => console.log(data));
When I run that, it prints this to the console:
{success: true, data: Array(15)}
When I edit the console.log above to output data.data like so:
fetch(yourAPIEndpoint).then(res => res.json()).then(data => console.log(data.data));
I get the array of locations that you are trying to set.
TL;DR: You need to set this.info = response.data.data.
Happy coding!

Related

Can get variable from secondary table in api on a list page, but not on single item page

This page reading and displaying a full table from an API works perfectly;
<template>
<b-col>
<h2>
Enrolments
<b-button :to="{ name: 'createEnrolment', params: { id: this.$route.params}}" variant="warning" class="float-right">Create</b-button>
</h2>
<b-card
v-for="enrolment in enrolments"
:key="enrolment._id"
>
<p>Course id:{{ enrolment.course.id }}</p>
<p>Course title:{{ enrolment.course.title }}</p>
<p>Status:{{ enrolment.status }}</p>
<p>Created:{{ enrolment.created_at }}</p>
<p>Date:{{ enrolment.date }}</p>
<p>Lecturer:{{ enrolment.lecturer.name }}</p>
<p>Lecturer email:{{ enrolment.lecturer.email }}</p>
<p>Updated:{{ enrolment.updated_at }}</p>
<p>Time:{{ enrolment.time }}</p>
<b-button :to="{ name: 'viewEnrolment', params: { id: enrolment.id}}" variant="warning">View</b-button>
</b-card>
</b-col>
</template>
<script>
import axios from '#/config'
export default {
name: "viewEnrolments",
components: {},
data(){
return {
enrolments: []
}
},
mounted() {
this.getData()
},
methods: {
getData() {
let token = localStorage.getItem('token')
axios
.get(`/enrolments`,
{
headers: {
"Accepted": `application/json`,
"Authorization": `Bearer ${token}`
}
})
.then(response => {
console.log(response.data)
this.enrolments = response.data.data
})
.catch(error => console.log(error))
}
}
}
</script>
However, when I try to view just one entry to the enrolments table, it cannot recognise or get that data from the courses table, giving the error:
"TypeError: Cannot read properties of undefined (reading 'id')", which comes from line 8: <p>Course id:{{ enrolment.course.id }}</p>
<template>
<b-col>
<h2>
Enrolments
</h2>
<b-card>
<p>Course id:{{ enrolment.course.id }}</p>
<p>Course title:{{ enrolment.course.title }}</p>
<p>Status:{{ enrolment.status }}</p>
<p>Created:{{ enrolment.created_at }}</p>
<p>Date:{{ enrolment.date }}</p>
<p>Lecturer:{{ enrolment.lecturer.name }}</p>
<p>Lecturer email:{{ enrolment.lecturer.email }}</p>
<p>Updated:{{ enrolment.updated_at }}</p>
<p>Time:{{ enrolment.time }}</p>
</b-card>
</b-col>
</template>
<script>
import axios from '#/config'
export default {
name: "viewEnrolment",
components: {},
data(){
return {
enrolment: []
}
},
mounted() {
this.getData()
},
methods: {
getData() {
let token = localStorage.getItem('token')
axios
.get(`/enrolments/${this.$route.params.id}`,
{
headers: {
"Accepted": `application/json`,
"Authorization": `Bearer ${token}`
}
})
.then(response => {
console.log(response.data)
this.enrolments = response.data.data
})
.catch(error => console.log(error))
},
}
}
</script>
I tried a few different ways to link the courses table to the enrolment one, but nothing worked. But I don't even understand what I have in the first one that allows me to reference the courses table, but not in the second.
I think you might have misspelled your variable in the second component, viewEnrolment. You have the plural enrollments:
this.enrolments = response.data.data
But in your component HTML, you're using the singular enrollment:
<p>Course id:{{ enrolment.course.id }}</p>
Also, this is just an additional tidbit: In Vue components, I would typically try to call API's in the created() hook, not the mounted() hook. created() comes before, and is called when data observation is set up, but before the component is actually mounted to HTML.
This could prevent weird issues where when trying to use this.enrolment in your HTML, it's not actually being available yet, because the API request isn't finished.

Vue Bootstrap Pagination Define :Total-rows

I am learning to paginate data returned from an API using AXIOS. I have a working set of code, but there is a place in the code defined by bootstrap for :Total-rows, this is currently hardcoded but this creates extra rows based on the value rather than a computed value. I want to calculate the number of rows dynamically.
I know that I can count the response data from the api using: this.variable = response.data.length, but the way I am calling the data is using page variable to paginate.
Any suggestions on an efficient way to accomplish this somewhat seemingly simple call?
<template>
<div id="app">
<div class="row">
<div class="col-md-12">
<li v-for="item in todos" :key="item.id">
{{ item.name }} : {{ item.type }}
</li>
</div>
</div>
<b-pagination size="md" :total-rows="54" v-model="currentPage" :per-page="10" #input="getPostData(currentPage)">
</b-pagination>
</div>
</template>
VUE
<script>
//Import axios for REST API calls
import axios from 'axios'
import 'regenerator-runtime/runtime';
//Import bootstrap CSS
import 'bootstrap/dist/css/bootstrap.css'
//Import bootstrap vue CSS
import 'bootstrap-vue/dist/bootstrap-vue.css'
const baseURL = 'http://localhost:3000/todos?_page='+this.currentPage+'&_limit='+this.limit;
export default {
name: 'app',
data () {
return {
title: 'Vue.js Pagination Example With Bootstrap',
currentPage: 1,
limit: 5,
todos: [],
todoName: "",
todoType: "",
}
},
methods: {
// Fetches todos when the component is created.
getPostData (currentPage) {
axios.get('http://localhost:3000/todos?_page='+this.currentPage+'&_limit='+this.limit)
.then(response => {
//console.log(response)
// JSON responses are automatically parsed.
this.todos = response.data
})
.catch(e => {
this.errors.push(e)
})
},
async addTodo() {
const res = await axios.post(baseURL, {
name: this.todoName,
type: this.todoType,
});
this.todos = [...this.todos, res.data];
//resets the input field
this.todoName = "";
this.todoType = "";
},
}, //end of methods
//detects the current page on load
mounted(currentPage){
this.getPostData(currentPage)
}
}
</script>
You will need the API to return the total amount of rows, otherwise your frontend have no way of knowing how many pages to show.
You can find an example of this below, which use a dummy/testing API called reqres. This API returns various information, like the current page, total amount of rows and per page and of course the data for the requested page.
new Vue({
el: "#app",
data() {
return {
currentPage: 1,
totalRows: 0,
perPage: 0,
users: [],
request: null
}
},
methods: {
async getData(page) {
const response = await fetch(`https://reqres.in/api/users?page=${page}&per_page=3`).then(resp => resp.json())
this.perPage = response.per_page;
this.users = response.data;
this.totalRows = response.total;
// Only for testing purposes
this.request = response
}
},
created() {
this.getData(this.currentPage)
}
})
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.18.1/bootstrap-vue.min.css" />
<script src="//cdn.jsdelivr.net/npm/vue#2.6.12/dist/vue.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.18.1/bootstrap-vue.min.js"></script>
<div id="app">
<b-pagination
v-model="currentPage"
:total-rows="totalRows"
:per-page="perPage"
#change="getData">
</b-pagination>
<ul>
<li v-for="{ first_name, last_name } in users">
{{ first_name }} {{ last_name }}
</li>
</ul>
Request
<pre>{{ request }}</pre>
</div>

I can't display properly v-data-table data: ''Invalid prop: type check failed for prop "items". Expected Array, got Object''

I'm starting a project in which I had to use Vue. I'm actually really new to this, so I'm learning on the go. I do apologize in advance since this question have answered before, however, I didn't really understand the solutions provided, which is why I'm here asking myself.
Well, I was trying to display some data on my Data Table (more specifically, v-data-table from Vuetify). I was able to get the data from the API, but, for some reason it doesn't show me anything. Thanks to Vuex I can see that the mutation worked because on the console on Google Chrome I can see the Array of objects. But as I said, it still does't show me a single thing on the table, it even says 'no data available'. Some errors that I get are things like '[Vue warn]: Invalid prop: type check failed for prop "items". Expected Array, got Object' and 'TypeError: this.items.slice is not a function'.
Here is the code from List.vue
<template>
<v-container id="data-tables" tag="section">
<div class="text-right">
<v-btn class="mx-2" fab dark color="primary" :to="{ name: 'UserCreate' }">
<v-icon dark>mdi-plus</v-icon>
</v-btn>
</div>
<base-material-card
color="indigo"
icon="mdi-vuetify"
inline
class="px-5 py-3"
>
<template v-slot:after-heading>
<div class="display-2 font-weight-light">
Lista de Empleados
</div>
</template>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
class="ml-auto"
label="Search"
hide-details
single-line
style="max-width: 250px;"
/>
<v-divider class="mt-3" />
<v-data-table
:headers="headers"
:items="users"
:search.sync="search"
:sort-by="['name', 'office']"
:sort-desc="[false, true]"
multi-sort
>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">
mdi-eye
</v-icon>
<v-icon
small
class="mr-2"
#click="editItem(item)"
:to="{ name: 'UserUpdate' }"
>
mdi-pencil
</v-icon>
<v-icon small #click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
</v-data-table>
</base-material-card>
</v-container>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'UsersTable',
data() {
return {
headers: [
{
text: 'Nombre',
value: 'empleado.nombre',
},
{
text: 'Apellido',
value: 'empleado.apellido',
},
{
text: 'Dirección',
value: 'empleado.direccion',
},
{
text: 'Correo Electrónico',
value: 'email',
},
{
text: 'Teléfono',
value: 'empleado.telefono',
},
{
sortable: false,
text: 'Actions',
value: 'actions',
},
],
loader: true,
search: undefined,
}
},
created() {
this.$store.dispatch('users/fetchUsers')
},
computed: {
...mapState(['users']),
},
methods: {},
mounted() {},
}
</script>
And the code from user.js, where the fetchUsers it's coming from.
import auth from '#/api/auth'
export const namespaced = true
export const state = {
users: [],
}
export const mutations = {
SET_USERS(state, users) {
state.users = users
},
}
export const actions = {
fetchUsers({ commit, dispatch }) {
auth
.getAllAccounts()
.then((response) => {
commit('SET_USERS', response.data)
})
.catch((error) => {
const notification = {
type: 'error',
message: 'There was a problem fetching users: ' + error.message,
}
dispatch('notification/add', notification, { root: true })
})
},
}
Thanks in advance.
You are not getting the correct user from vuex, because is namespaced, change to:
computed: {
...mapState('users',['users']),
},
MapState helper dosen't work the same way like the other helpers because the state module isn't registred in the global namespace. So namespacing your module will help or you do it in this way:
computed: {
...mapState({
users: state => state.FilenameOfYourModule.users
})
}

Issue trying to output WordPress post data with Vue

Can anyone tell me why I am getting an error when trying to output the post data?
I've tried adding params like title, content but nothing works. I might add that I can get it to work using the id.
<template>
<section class="container animated fadeIn">
<nuxt-link :to="{ name: 'blog' }">
Back to Blog
</nuxt-link>
<div>
<h1 class="title">
{{ post }} <!-- this returns an array of the post data -->
{{ slug }} <!-- this returns the slug -->
{{ post.title }} <!-- this returns an error _vm.post is undefined -->
</h1>
</div>
</section>
</template>
<script>
import axios from 'axios'
export default {
name: 'post',
data () {
return {
slug: this.$route.params.slug,
post: {},
error: []
}
},
asyncData ({ params }) {
return axios.get(`http://localhost/amcdwp/wp-json/wp/v2/posts/?slug=${params.slug}`)
.then((response) => {
return { post: response.data }
})
}
}
</script>
I want to output individual data like post.title. I get an error of _vm.post is undefined

Vue2.js and Firebase looping with v-for and :key can't get value

Looping through a Firebase project using v-for and having a devil of a time trying to get one value (imgurl) for each item. Here are a couple rows form the firebase object:
firebase data
Here is my script code in App.vue.
<script>
import Firebase from "firebase";
let config = {
…all correct blah blah…
};
let app = Firebase.initializeApp(config);
let db = app.database();
let itemsRef = db.ref("tblItems");
export default {
name: "app",
firebase: {
items: itemsRef
},
data() {
return {
styleObject: {
backgroundImage: "" //RIGHT IN HERE I NEED item.imgname BUT CAN'T FIGURE OUT SYNTAX.
}
};
}
};
Here is where I loop through using v-for and key and I call StyleObject.
<template>
<div id="app">
<div v-for="item in items" :key="item.id" v-bind:style="styleObject">
<h1>{{ item.title }}</h1>
<h2>{{ item.author }}</h2>
</div>
<router-view/>
</div>
</template>
Everything works fine, except I can't figure you how to get item.imgname where the RIGHT IN HERE comment is so I can use it for a background image in a style= attribute (each div has its own background image). Any help much appreciated.
In my opinion the easiest way to do that is to make de div into a new component and to pass the image name as a prop into it. Your component will then look like this:
Vue.component('exampleDiv', {
props: ['imgname','title','author'],
template: '<div :style="styleObject">
<h1>{{ title }}</h1>
<h2>{{ author }}</h2>
</div> ',
data() {
return {
styleObject: {
backgroundImage: this.imgname
}
}
})
You can then call the component in your code like this:
<template>
<div id="app">
<exampleDiv v-for="item in items"
:key="item.id" :title="item.title"
:author="item.author" :imgname = "item.imgname">
</exampleDiv>
<router-view/>
</div>
</template>

Categories

Resources