VueJS - unable to read property of undefined - javascript

I'm attempting to load Youtube Videos via their API, and in order to get the duration, I have to send a second API request. The issue I'm having is that sometimes this code works, sometimes it throws
Error in render: "TypeError: Cannot read property 'contentDetails' of undefined"
I think it is because when Vue is trying to access the property, it is not there. If I access the variable (resolvedVideoData) with the Vue Developer tool on a failed load, it has only resolved ONE video.
What am I doing wrong? I'm calling the API query in created(), so the DOM hasn't loaded yet?
<script>
import axios from 'axios';
import moment from 'moment'
export default {
name: 'BlogGrid',
data(){
return {
videos:[],
base_url_youtube: 'http://www.youtube.com/watch?v=',
videoId: [],
resolvedVideoData: [],
}
},
watch: {
videos: function (){
var arrayLength = this.videos.length;
for (var i = 0; i < arrayLength; i++) {
this.videoId.push(this.videos[i].id.videoId);
}
this.getVideoDuration()
}
},
created() {
axios.get(`https://www.googleapis.com/youtube/v3/search?key={{YOUTUBE_API_KEY}}&channelId={{ChannelID}}A&order=date&part=snippet%20&type=video,id&maxResults=50`)
.then(response => {
this.videos = response.data.items
})
.catch(e => {
this.errors.push(e)
})
},
methods: {
getVideoDuration(){
axios.get('https://www.googleapis.com/youtube/v3/videos?id=' + this.videoId.join(', ') + '&part=contentDetails&key={{YOUTUBE_API_KEY}}')
.then(response => {
this.resolvedVideoData = response.data.items
})
.catch(e => {
this.errors.push(e)
})
}
}
}
</script>
Upon further checking, if I spam the generated URL for the Google API, it seems like sometimes it's returning one value, sometimes it's returning all the values.
Since it was asked for
<section class="blog-area ptb-80">
<div class="container">
<div class="row">
<div
v-for="(item, index) in videos"
:key="item.etag"
class="col-lg-4 col-md-6"
>
<div class="single-blog-post">
<div
class="blog-image"
:href="base_url_youtube + item.id.videoId "
>
<a :href="base_url_youtube + item.id.videoId ">
<img
:src="item.snippet.thumbnails.high.url"
name="videoicon"
alt="image"
></a>
<a :href="base_url_youtube + item.id.videoId ">
<div class="overlay hidden-sm">
<div class="text">
{{ parseDuration(resolvedVideoData[index].contentDetails.duration) }}
<br>
<a :href="base_url_youtube + item.id.videoId"> <feather type="monitor" /></a>
</div>
</div>
</a>
<div class="date">
<feather type="calendar" />
{{ format_date(item.snippet.publishedAt ) }}
</div>
<div class="blog-post-content">
{{ item.snippet.title }}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
with my other methods being.
format_date(value){
if (value) {
return moment(String(value)).format('DD-MM-YYYY')
}
},
parseDuration(e){
var n = e.replace(/D|H|M/g,":").replace(/P|T|S/g,"").split(":");
if(1 == n.length)
2!=n[0].length && (n[0]="0"+n[0]),n[0]="0:"+n[0];
else
for(var r=1, l=n.length-1; l>=r;r++)
2!=n[r].length && (n[r]="0"+n[r]);
return n.join(":")
},

Related

how can i do a call to axios wtihout fall in a infinite loop

i did a netflix copy with two different Axios call for series and films... Now i want add an Axios call for actors... and i did it... but i fall in an infinite loop, so after one minute the page crash... do you know why???
This is a CardFilm.vue that is repeated with a v-for in MainFilm.vue...
In my card i ve got all Film info from film's API... i want add a section for actors, so i m taking another API for actors... i had back 20 objects where i take just the element.name (actor name), with this.arrayAttori.length i take just the first 5 element of the array ACTORS of API, but after that it work i fall in an infinite loop, because my code ask infinite time the ACTORS ARRAY of API
TEMPLATE
<template>
<div class="card p-4 col-2 text-center text-white bg-black">
<img
v-show="filmData.poster_path != null"
:src="'http://image.tmdb.org/t/p/w342/' + filmData.poster_path"
:alt="filmData.title"
/>
<h3>{{ filmData.title }}</h3>
<h5
v-show="
filmData.title.toLowerCase() != filmData.original_title.toLowerCase()
"
>
{{ filmData.original_title }}
</h5>
<lang-flag
class="flag"
:iso="filmData.original_language"
:squared="false"
/>
<h4>{{ filmData.vote_average }}</h4>
<div>
<div class="star" v-for="element in fullArrayStars()" :key="element">
&starf;
</div>
<div
class="actor"
v-for="element in ricercaAttori /*()*/"
:key="element.name"
>
{{ element.name }}
</div>
</div>
</div>
</template>
<script>
import LangFlag from "vue-lang-code-flags";
import axios from "axios";
export default {
name: "CardBool",
data() {
return {
starf: "star",
arrayStars: [],
stars: Math.floor(this.filmData.vote_average / 2),
arrayAttori: null,
};
},
components: {
LangFlag,
},
props: {
filmData: Object,
},
methods: {
fullArrayStars() {
return this.stars;
},
ricercaAttori() {
axios
.get(
`https://api.themoviedb.org/3/movie/${this.filmData.id}/credits?api_key=9631e84004e35c8371fcb3c009af9551`
)
.then((response) => {
this.arrayAttori = response.data.cast;
});
this.arrayAttori.length = 5;
console.log(this.arrayAttori);
return this.arrayAttori;
},
},
};
</script>
i m working in VUE CLI...
thanks to everyone
methods: {
fullArrayStars() {
return this.stars;
},
changeVisibilita() {
if (this.visibilita == false) {
this.visibilita = true;
} else {
this.visibilita = false;
}
},
},
created() {
axios
.get(
`https://api.themoviedb.org/3/movie/${this.filmData.id}/credits?api_key=9631e84004e35c8371fcb3c009af9551`
)
.then((response) => {
this.arrayAttori = response.data.cast;
})
.then(() => {
this.arrayAttori.splice(5, this.arrayAttori.length);
});
return this.arrayAttori;
},
this is the solution... you have to use created() so you can do the call to the API before the creation of the Card, and you have to use then two times...
one time to create the array and the second time to work in that array, in this case to splice it

Vue.js: Vue-Carousel jumping to last item in array on click

I am currently working on an application that involves scrolling through members from two different lists. Once you click one you see their image and bio. We added up and down arrows to scroll through their biography, but when i click the down arrow it seems that the active class on the carousel slide now jumps to the last member in my array, but still displays the member i was already on. To add to that I cannot swipe back and forth once i am on a member (this is something that I could do previously.) I know this probably isn't the best description and there is only so much code I can show, as I cannot display the entire application.
I wanted to also note that if I refresh my page, everything works as expected. Does this mean something isn't initializing correctly?
this is the code specific to the page i am talking about:
<template>
<div class="member-info-carousel">
<div class="header">
<h2 v-if="founderChairman === true">Member List One</h2>
<h2 v-else>Member List Two</h2>
<img src="../assets/logo.png" alt="Logo" />
</div>
<carousel :minSwipeDistance="384" :perPage="1" :paginationEnabled="false" :navigationEnabled="true" navigationNextLabel="<i>NEXT</i>"
navigationPrevLabel="<i>BACK</i>" :navigateTo="selectedListIndex" #pagechange="OnPageChange">
<slide v-for="(member, index) in selectedList" :key="index">
<div class="member-bio-page" :member="member" v-on:showContent="showContent">
<div class="bio">
<div class="portrait-image">
<img :src="member.imgSrc" />
</div>
<div class="bio-container">
<div class="inner-scroll" v-bind:style="{top: scrollVar + 'px'}">
<div class="english"></div>
<div class="pin-name">
<img :src="member.pin" />
<h1>{{ member.name }}</h1>
</div>
<div class="description-container">
<div class="para">
<p class="quote" v-html="member.quote"></p>
<p v-html="member.bio"></p>
<div class="spanish"></div>
<p class="quote" v-html="member.spanishQuote"></p>
<p v-html="member.spanishBio"></p>
</div>
</div>
</div>
</div>
<div class="scroll-buttons">
<div>
<!-- set the class of active is the scroll variable is less than 0-->
<img class="btn-scroll" v-bind:class="{ 'active': scrollVar < 0 }" #click="scrollUp" src="#/assets/arrow-up.png">
</div>
<div>
<!-- set the class of active is the scroll variable is greater than the height of the scrollable inner container-->
<img class="btn-scroll" v-bind:class="{ 'active': scrollVar > pageChangeHeight }" #click="scrollDown" src="#/assets/arrow-down.png">
</div>
</div>
<div class="eng-span">
English
Español
</div>
</div>
<div class="play-button">
<!-- if the array members has a property of video, then the play button will show on the slide. If not it will not show the image -->
<img v-if="member.hasOwnProperty('video')" #click="showContent" src="#/assets/play-button.png">
</div>
</div>
<!-- <MemberBioPage :member="member" v-on:showContent="showContent"/> -->
</slide>
</carousel>
<modal name="video-modal"
:width="1706"
:height="960">
<video width="1706" height="960" :src="(selectedList && selectedList[this.currentPage]) ? selectedList[this.currentPage].video : ''" autoplay />
</modal>
<div class="footer-controls">
<div class="footer-bar">
<p>Tap Back or Next to view additional profiles.</p>
<p>Tap the arrows to scroll text up or down.</p>
</div>
<div class="nav-container">
<img class="nav-bubble" src="#/assets/navigation-bubble-bio-page.png" alt="An image where the back, next and close button sit" />
</div>
<button class="close-button" #click="closeInfo">
<img src="#/assets/x-close-button.png" />
CLOSE
</button>
</div>
</div>
</template>
<script>
import { Carousel, Slide } from 'vue-carousel'
export default {
data () {
return {
currentPage: 0,
pageChangeHeight: -10,
scrollVar: 0
}
},
components: {
// MemberBioPage,
Carousel,
Slide
},
mounted () {
this.enableArrows()
},
updated () {
this.enableArrows()
},
computed: {
selectedList () {
return this.$store.state.selectedList
},
selectedListIndex () {
return this.$store.state.selectedListIndex
},
founderChairman () {
return this.$store.state.founderChairman
}
},
methods: {
enableArrows () {
var outerHeight
var innerHeight
if (document.querySelectorAll('.VueCarousel-slide-active').length > 0) {
outerHeight = document.querySelectorAll('.VueCarousel-slide-active .bio-container')[0].clientHeight
innerHeight = document.querySelectorAll('.VueCarousel-slide-active .inner-scroll')[0].clientHeight
} else {
outerHeight = document.querySelectorAll('.VueCarousel-slide .bio-container')[0].clientHeight
innerHeight = document.querySelectorAll('.VueCarousel-slide .inner-scroll')[0].clientHeight
}
this.pageChangeHeight = outerHeight - innerHeight
return this.pageChangeHeight
},
scrollUp () {
this.scrollVar += 40
console.log(this.scrollVar += 40)
},
scrollDown () {
this.scrollVar -= 40
console.log(this.scrollVar)
},
OnPageChange (newPageIndex) {
this.scrollVar = 0
this.currentPage = newPageIndex
this.pageChangeHeight = -10
},
closeInfo () {
if (this.$store.state.selectedList === this.$store.state.foundersList) {
this.$store.commit('setSelectedState', this.$store.state.foundersList)
this.$router.push({ name: 'Carousel' })
} else if (this.$store.state.selectedList === this.$store.state.chairmanList) {
this.$store.commit('setSelectedState', this.$store.state.chairmanList)
this.$router.push({ name: 'Carousel' })
}
},
showContent () {
this.$modal.show('video-modal')
},
toEnglish () {
this.scrollVar = 0
},
toSpanish () {
var spanishPos
if (document.querySelectorAll('.VueCarousel-slide-active').length > 0) {
spanishPos = document.querySelectorAll('.VueCarousel-slide-active .spanish')[0].offsetTop
} else {
spanishPos = document.querySelectorAll('.VueCarousel-slide .spanish')[0].offsetTop
}
this.scrollVar = -spanishPos
}
}
}
</script>
This is my store index file:
import Vue from 'vue'
import Vuex from 'vuex'
import chairmans from '#/data/chairmans-club'
import founders from '#/data/founders-circle'
import memoriam from '#/data/in-memoriam'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
finishedLoading: false,
transitioning: false,
foundersList: founders,
chairmanList: chairmans,
selectedList: founders,
selectedListIndex: -1,
founderChairman: true,
inMemoriam: memoriam,
idleTimer: {
id: null,
duration: 1
},
idleTimeoutModal: {
show: false,
duration: 1
}
},
mutations: {
setSelectedState (state, list) {
state.selectedList = list
},
setSelectedListIndex (state, idx) {
state.selectedListIndex = idx
},
showIdleTimeoutModal (state, value) {
state.idleTimeoutModal.show = value
},
founderChairmanClicked (state, data) {
state.founderChairman = data
},
setInMemoriam (state, content) {
state.inMemoriam = content
}
},
actions: {
restartIdleTimer ({ state, commit }) {
clearTimeout(state.idleTimer.id)
state.idleTimer.id = setTimeout(() => {
commit('showIdleTimeoutModal', true)
}, state.idleTimer.duration * 1000)
},
stopIdleTimer ({ state }) {
clearTimeout(state.idleTimer.id)
}
}
})
export default store
and an example of the data i am pulling:
const event = [
{
index: 0,
name: 'Member Name',
carouselImage: require('#/assets/carousel-images/image.jpg'),
drawerImage: require('#/assets/drawer-images/drawerimage.jpg'),
imgSrc: require('#/assets/bio-images/bioimage.jpg'),
quote: '“quote.”',
spanishQuote: `“spanish quote.”`,
bio: '<p>bio copy here</p>',
spanishBio: '<p>spanish bio copy</p>',
pin: require('#/assets/pin.png')
}
]
export default event

How to parse json in Vue js to use it in template

<template>
<div class="row w-20 " >
<div class="card col-4 ">
<ul>
<li v-for="message in conversations.messages" :key="message.messages">
{{message.text}}
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: ['conversation_index_route'],
data() {
return {
conversations: [],
url: 'http://127.0.0.1:8000/comms/conversation/',
}
},
beforeMount() {
this.$eventBus.$on('selectConversation', this.getConversations)
this.getConversations();
},
methods: {
getConversations(id) {
console.log(this.url+id);
axios.get(this.url+ id)
.then(response => {
this.conversations = response.data;
this.conversations = JSON.parse(this.conversations.message);
console.log(this.conversations);
});
}
}
}
</script>
conversation_index_route:"http://127.0.0.1:8000/comms/conversation"
conversations:Object
all_staff_attended:false
centre_id:5
children:""
classes:""
cover_image:"https://via.placeholder.com/150x150"
created_at:"2020-05-30 19:01:59"
exited_users:null
id:257
last_id:null
messages:Array[1]
0:"{"_id":1854,"text":"This is the beginning of this conversation","createdAt":"2020-05-30 19:01:59","system":true}"
parent_users:"3016"
parents:Array[1]
staff_users:"180,181"
staffs:Array[2]
status_id:1
title:"Test"
updated_at:"2020-05-30 19:01:59"
url:"http://127.0.0.1:8000/comms/conversation/"
So what shall I code to use the message text in my template to display the text messages?
You'd have to do JSON.parse(conversations.messages[0]).text. This way you parse the object inside messages and have access to its properties.
Simply JSON.parse the string
var myJson = "[{\"id\":72,\"food_item_id\":\"56\",\"variation_id\":\"20\",\"price\":\"50\",\"created_at\":\"2021-06-29T05:29:14.000000Z\",\"updated_at\":\"2021-06-29T05:29:14.000000Z\",\"variant\":null}]";
var myJson2 = JSON.parse(myJson);
console.log(myJson2);

Getting console forEach of undefined error with vue.js v-for functionality and axios

I am attempting to load calendar events from an API and then display them using Axios and Vue.js. It is working, but I am getting the following error in the console:
[Vue warn]: Error in render: "TypeError: Cannot read property
'forEach' of undefined"
My vue file is below. The content renders fine but every time the site loads the above warning shows up. Must be something simple I am missing.
<template>
<div class="row row-equal">
<div class="flex xs12">
<va-card
no-padding-h
style="overflow-x: auto;"
:title=title>
<va-timeline style="min-width: 400px;" v-if="meetings">
<va-timeline-item v-for="event in meetings">
<template slot="before">
<div
class="title text--center"
:style="{color: $themes.success}">
{{ event.start }} - {{ event.end }}
</div>
<div class="va-timeline-item__description">
{{ event.summary }}
</div>
<div class="va-timeline-item__description">
{{ event.creator }}
</div>
</template>
</va-timeline-item>
</va-timeline>
</va-card>
</div>
</div>
</template>
<script>
var moment = require('moment')
export default {
name: 'dashboard-calendars',
props: ['title'],
data () {
return {
meetings: [],
}
},
methods: {
loadCalendars () {
this.$http.get('calendar?name=' + this.$props.title + '&format=json')
.then(response => {
this.meetings = response.data
})
.catch(error => {
// handle error
console.log(error)
})
},
},
mounted () {
this.loadCalendars()
},
}
</script>
<style scoped>
.title {
font-size: 1.0em;
}
</style>
<style>
.va-card__header-title{
font-size: 1.0em !important;
}
</style>
The v-for was still being executed as the v-if='meetings' was allowing the loop to continue so using #pascalLamers response I added the length check and that worked.
<va-timeline-item v-for="event in meetings">
<template slot="before">
<div
class="title text--center"
:style="{color: $themes.success}">
{{ event.start }} - {{ event.end }}
</div>
<div class="va-timeline-item__description">
{{ event.summary }}
</div>
<div class="va-timeline-item__description">
{{ event.creator }}
</div>
</template>
</va-timeline-item>
</va-timeline>```
I would try it this way, put it in the created lifecycle hook and make it async:
var moment = require('moment')
export default {
name: 'dashboard-calendars',
props: ['title'],
data () {
return {
meetings: [],
}
},
async created() {
await this.$http.get('calendar?name=' + this.$props.title + '&format=json')
.then(response => {
this.meetings = response.data
})
.catch(error => {
// handle error
console.log(error)
})
},
}

PHP API WITH VUE.JS

I Created my API with PHP and here is the link: https://monstajams.co/streaming/rest/api/album/read.php
But anytime i put it in my Vue.js (Home.vue) file using axios No data is displayed on the front-end.
Here is my code below:
<ul class="ta-track-list" v-if="faqs && faqs.length">
<li class="ta-track-card column col-2 flex-column" v-for="faq of faqs">
<div class="inner">
<div class="artwork" role="link">
<span role="link" style="background-image: url(http://localhost/mymusic/assets/images/artwork/Wizkid-Soco.jpg);">
</span>
<div class="hover flex align-center justify-center">
<button id="webutton" class="ta-secondary play" onclick='playSong()'>
<i class="material-icons">play_arrow</i>
</button>
</div>
</div>
<div class="info">
<div class="title white-primary-hover" role="lin">{{ faqs }}</div>
<div class="username light-white-hover" role="link">{{ faq.artist }}</div>
<div class="released">{{ faq.duration }}</div>
</div>
</div>
</li>
</ul>
<script>
import axios from 'axios';
export default {
name: 'home',
data: () =>({
faqs: [],
errors: []
}),
created() {
axios.get('https://monstajams.co/streaming/rest/api/album/read')
.then(response => {
this.faqs = response.data;
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>
The problem is your code incorrectly assumes axios.get() resolves to the raw response, but it actually resolves to a response wrapper, where the raw response is contained in a data subproperty of the wrapper, which coincidentally has the same name as the target property within the response.
You can either change your Axios response handler to get the inner data field:
axios.get('https://monstajams.co/streaming/rest/api/album/read')
.then(response => {
// this.faqs = response.data; // response.data is the raw response, but you need the array within the response (also named "data")
this.faqs = response.data.data;
})
demo
Or leave your frontend alone, and update your PHP backend to send only the array in the response:
// FROM THIS RESPONSE:
{
data: [/* PLAYLIST DATA */]
}
// TO THIS RESPONSE:
[/* PLAYLIST DATA */]
You are not updating your data accordingly to Vue docs.
For reactive changes see this document.
In the example below i update my list before Vue is mounted so rendering can occur accordingly.
let vm = new Vue({
el: "#app",
data: {
todos: []
},
methods: {
updateList() {
axios.get('https://monstajams.co/streaming/rest/api/album/read')
.then(res => {
res.data.data.forEach(item => {
Vue.set(vm.todos, vm.todos.length, {
text: item.title,
done: true
})
})
})
}
},
beforeMount() {
this.updateList()
},
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<span>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>

Categories