Reputation: 577
I'm trying to call an API and once I have the response I want to target the image_url property but I'm getting this error Error in render: "TypeError: Cannot read property 'image_url' of undefined"
and Cannot read property 'image_url' of undefined at VueComponent.getImages1 (Info.vue?42ed:39)
I've tested my code in another Javascript file and over there it works but for some reason not here. I've also checked for if the parent state is sending data correctly to child state through Vue console and it has worked.
Info.vue
<template>
<section>
<img :src="getImages1()" alt="./assets/notFound.png" />
<img :src="getImages2()" alt="./assets/notFound.png" />
</section>
</template>
<script>
import axios from "axios";
export default {
props: {
anime1: String,
anime2: String,
},
methods: {
animeFind(anime) {
axios
.get(`https://api.jikan.moe/v3/search/anime?q=${anime}`)
.then(async function(response) {
const id = await response.data["results"][0]["mal_id"];
await axios
.get(`https://api.jikan.moe/v3/anime/${id}`)
.then(function(response) {
return response.data;
})
.catch(function(error) {
return error; // take care of this later
});
})
.catch(function(error) {
return error; // take care of this later
});
},
// eslint-disable-next-line no-unused-vars
getImages1() {
let response = this.animeFind(this.anime1);
return response["image_url"];
},
getImages2() {
let response = this.animeFind(this.anime2);
return response["image_url"];
},
},
};
</script>
<style></style>
I tried doing this and it worked
main.js
const axios = require("axios");
const animeFind = (anime) =>
axios
.get(`https://api.jikan.moe/v3/search/anime?q=${anime}`)
.then(async function (response) {
const id = await response.data["results"][0]["mal_id"];
await axios
.get(`https://api.jikan.moe/v3/anime/${id}`)
.then(function (response) {
console.log(response.data["image_url"]);
})
.catch(function (error) {
console.log(error);
});
})
.catch(function (error) {
console.log(error);
});
animeFind("Naruto");
animeFind("Cowboy Bebop");
This is the parent component, when the button is clicked only then should the image change
<template>
<section class="hero">
<div class="parent-1">
<h1 class="title is-1">Compare two animes! :)</h1>
</div>
<div class="columns">
<div class="column">
<b-field class="label" label="Anime 1">
<b-input value="Enter the first anime!" v-model="anime1"></b-input>
</b-field>
</div>
<div class="column">
<b-field class="label" label="Anime 2">
<b-input value="Enter the second anime!" v-model="anime2"></b-input>
</b-field>
</div>
</div>
<div class="button-spacing">
<b-button class="button" type="is-primary" @click="checkComplete"
>Compare!</b-button
>
</div>
<Info :anime1="anime1" :anime2="anime2" v-if="success">Wow</Info>
</section>
</template>
<script>
import Vue from "vue";
import Buefy from "buefy";
import "buefy/dist/buefy.css";
import Info from "./Info.vue";
Vue.use(Buefy);
export default {
components: {
Info,
},
data() {
return {
anime1: "",
anime2: "",
success: false,
};
},
methods: {
// log() {
// console.log(this.anime1);
// console.log(this.anime2);
// },
checkComplete() {
if (this.anime1.length > 0 && this.anime2.length > 0) {
// let animeData1 = this.animeFind(this.anime1);
// let animeData2 = this.animeFind(this.anime2);
this.success = true;
return this.$buefy.toast.open({
message: "Yay, just a moment now!",
type: "is-success",
position: "is-bottom",
duration: 3000,
});
}
this.success = false;
return this.$buefy.toast.open({
duration: 3000,
message: `Please fill out both fields`,
position: "is-bottom",
type: "is-danger",
});
},
},
};
</script>
Upvotes: 1
Views: 1458
Reputation: 41
The getImages()
function return before the animeFind()
would return. So the getImages()
will return undefined.
You can put the axios
call into hooks and when you return the response.data object, you can assign it to a property in the data object. You use this property instead the function in the template, so the component will be reactive.
Notice that you should use regular function on the outer function in the axios call and arrow functions on the then()
responses for getting a proper this
.
I am taking care of only one image example for simplicity, but editing this is not so complicated.
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<section>
<p>Show image here</p>
<img :src="urlResponse['image_url']" alt="./assets/notFound.png">
</section>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
urlResponse: {}
};
},
props: {
anime1: String,
anime2: String
},
created() {
this.animeFind(this.anime1);
},
updated() {
this.animeFind(this.anime1);
},
methods: {
animeFind: function(anime) {
axios
.get(`https://api.jikan.moe/v3/search/anime?q=${anime}`)
.then(async response => {
const id = await response.data["results"][0]["mal_id"];
await axios
.get(`https://api.jikan.moe/v3/anime/${id}`)
.then(response => {
this.urlResponse = Object.assign(
{},
this.urlResponse,
response.data
);
return response.data;
})
.catch(function(error) {
return error; // take care of this later
});
})
.catch(function(error) {
return error; // take care of this later
});
}
}
};
</script>
<style></style>
Upvotes: 1
Reputation: 728
I think you're still a little confused with promises. your animFind function is not returning anything.
Instead try
<template>
<section>
<img :src="url1" alt="./assets/notFound.png" />
<img :src="url2" alt="./assets/notFound.png" />
</section>
</template>
<script>
import axios from "axios";
export default {
props: {
anime1: String,
anime2: String,
},
data() {
return {
url1: '',
url2: '',
error: ''
}
},
methods: {
animeFind(anime, data) {
axios
.get(`https://api.jikan.moe/v3/search/anime?q=${anime}`)
.then(response => {
const id = response.data["results"][0]["mal_id"];
axios
.get(`https://api.jikan.moe/v3/anime/${id}`)
.then(response => this[data] = response.data["image_url"]);
})
.catch(error => {
this.error = error; // take care of this later
});
}
},
watch: {
anime1: {
immediate: true,
handler(newVal, oldVal) {
this.animeFind(newVal, 'url1');
},
},
anime2: {
immediate: true,
handler(newVal, oldVal) {
this.animeFind(newVal, 'url2');
},
},
},
};
</script>
Notice the use if arrow functions to stay in the vue scope
Upvotes: 2