Unable to fetch data using the Flickr API - vue.js

I'm trying to build a simple photo gallery web application using Vue JS as a way to learn Vue JS. I am attempting to use the Flickr API (https://api.flickr.com/services/feeds/photos_public.gne?format=json) in my web app and I'm trying to fetch data from the above URL but unable to. The following is my code. It's still work in progress hence why its missing a lot things and I just want to see the response in the console.
<template>
<div class="container">
<div>
<h1>TEST</h1>
<tbody>
<td v-for="(image, index) in images" :key="index">
{{ image }}
</td>
</tbody>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "ImageFeed",
data() {
return {
images: [],
};
},
methods: {},
computed: {},
mounted() {
axios
.get(
"https://api.flickr.com/services/feeds/photos_public.gne?format=json"
)
.then((response) => {
console.log(response);
});
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>
I get the following console error:
however when I test in Postman, I get the following response, which is the end goal:
I would appreciate any help!

Related

Vue: unexpected token: ':' in image path

I'm passing an image path to a child Vue component but get:
unexpected token: ':' in https://path.to.image/image.jpg
I've created a Vue sandbox that illustrates the problem.
HTML
<div class="js-product-map">
<product-map
:logo="https://path.to.image/image.jpg"
/>
</div>
Component
<template>
<div>
<img :src="image" />
</div>
</template>
<script>
export default {
props: {
logo: {
type: String,
required: true,
},
},
computed: {
image() {
return this.logo;
},
},
};
</script>
What am I doing wrong?
Can you clarify what it is you're trying to do exactly? Because it's not entirely clear to me.
The error you're getting is coming from this:
<div class="js-product-map">
<product-map
:logo="https://path.to.image/image.jpg"
/>
</div>
It's complaining about the : on the logo attribute. But I question why you're doing it this way to begin with. Typically you only want a single selector in index.html as the root of your app. Unless you're trying to have multiple Vue instances.
The way that you have your ProductMap component set up in your sandbox seems like it would accomplish what you're trying to do in index.html.

Use axios together with `nuxt/content` in a component's `fetch` method

I´m creating a static site using #nuxt/content and I´m trying to create some components that will be used from the markdown. One of those components needs to call external API to retrieve some data that is shown in the HTML.
These are the relevant snippets, trying to show a card with information from a GitHub repository:
blog_article.md
In <content-github-repository repo="jgsogo/qt-opengl-cube">this repository</content-github-repository> there is...
content/github/Repository.vue
<template>
<span class="">
<a :href="`https://github.com/${repo}`">
<slot>{{ repo }}</slot>
</a>
<div>{{ info.description }}</div>
</span>
</template>
<script>
export default {
props: {
repo: {
type: String,
require: true,
},
},
data: () => ({
info: null,
}),
async fetch() {
console.log(`Getting data for repo: ${this.repo}`);
this.info = (await this.$axios.get(`https://api.github.com/repos/${this.repo}`)).data;
},
};
</script>
The error I get is Cannot read properties of null (reading 'description'), it is like the this.info is not being populated before rendering the HTML... but it runs, because I get the output from the console.log. Maybe I misunderstood the docs about fetch, is there any other way to achieve this behavior?
Thanks!
Check out the docs here. From the looks of it, Nuxt can mount the component before fetch finishes, so you need to guard against that data not being set when accessing it in your component.
Something as simple as the following should work:
<template>
<span class="">
<a :href="`https://github.com/${repo}`">
<slot>{{ repo }}</slot>
</a>
<p v-if="$fetchState.pending">Fetching info...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>{{ info.description }}</div>
</span>
</template>

How to have list + details pages based on API fetched content

I am facing a issue in my nuxt projct.
when i route the page by using nuxt-link, it doesn't render component in my page, i guess this is not making fetch call.
but when i use normal a href link, my page is working fine. everything is in place.
here is the link in a blog listing page component
// blog listing page snippet
<template>
<div>
<div v-for="blog in blogs.response.posts" :key="blog.id" class="col-md-3">
<nuxt-link :to="`/blogs/${blog.id}`" class="theme-blog-item-link"> Click to View Blog </nuxt-link>
</div>
</div>
</template>
<script>
export default {
data() {
return {
blogs: [],
}
},
async fetch() {
this.blogs = await fetch('https://www.happyvoyaging.com/api/blog/list?limit=4').then((res) => res.json())
},
}
</script>
but this works fine with if i replace nuxt-link with a href tag
<a :href="`/blogs/${blog.id}`" class="theme-blog-item-link">
Click to View Details
</a>
By click to that link, i want to view the detail of the blog by given id. that is _id.vue, code for that page is below.
//This is Specific Blog Details page code
<template>
<div class="theme-blog-post">
<div v-html="blogs.response.description" class="blogdesc"></div>
</div>
</template>
<script>
export default {
data(){
return {
blogs: []
}
},
async fetch() {
const blogid = this.$route.params.id
this.blogs = await fetch('https://www.happyvoyaging.com/api/blog/detail?id='+blogid+'').then((res) => res.json())
},
}
</script>
problem is on blogdetails page, where routing through nuxt-link not rendering the components but by normal a href link, it works fine
I am getting this error in console
vue.runtime.esm.js?2b0e:619 [Vue warn]: Unknown custom element: <PageNotFound> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
found in
---> <Error> at layouts/error.vue
<Nuxt>
<Layouts/default.vue> at layouts/default.vue
<Root>
Since your API requires some CORS configuration, here is a simple solution with the JSONplaceholder API of a index + details list collection.
test.vue, pretty much the blog listing in your case
<template>
<div>
<div v-if="$fetchState.pending">Fetching data...</div>
<div v-else>
<div v-for="item in items" :key="item.id">
<nuxt-link :to="`/details/${item.id}`"> View item #{{ item.id }}</nuxt-link>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [],
}
},
async fetch() {
const response = await fetch('https://jsonplaceholder.typicode.com/users')
this.items = await response.json()
},
}
</script>
details.vue, this one needs to be into a pages/details/_id.vue file to work
<template>
<div>
<button #click="$router.push('/test')">Go back to list</button>
<br />
<br />
<div v-if="$fetchState.pending">Fetching details...</div>
<div v-else>{{ details.email }}</div>
</div>
</template>
<script>
export default {
data() {
return {
details: {},
}
},
async fetch() {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${this.$route.params.id}`)
this.details = await response.json()
},
}
</script>
As you can see, I do only use async/await here and no then for consistency and clarity.
Try this example, then see for fixing the CORS issue. The latter is backend only and not anyhow related to Nuxt. A quick google search may give you plenty of results depending of your backend.

How do I call the API with Axios on the (child) component and present the result on the Page (parent) component in Nuxt?

If I run this code on the Page component (mountains.vue) it works and I get the data from the API with help with Axios:
<template>
<div>
<ul>
<li v-for="(mountain, index) in mountains" :key="index">
{{ mountain.title }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
mountains: [],
};
},
async asyncData({ $axios }) {
const mountains = await $axios.$get("https://api.nuxtjs.dev/mountains");
return { mountains };
},
};
</script>
But I want to put this code in a component (MountainsList) and do the Axios call in the component (MountainsList), but display the data on the Page component (mountains.vue) by injecting the component in Nuxt like this:
<template>
<MountainsList />
</template>
Now when I run the code, the data using Axios doesn't appear anymore... So how do I inject the data to the Page component above doing the Axios call in the child component?
asyncData only works on a page
From the docs
asyncData is called every time before loading the page component
One way you can accomplish what you want is passing the mountains in as a prop to the MountainList component. Something like below...
<template>
<MountainList :mountains="mountains" />
</template>
<script>
export default {
async asyncData({ $axios }) {
const mountains = await $axios.$get("https://api.nuxtjs.dev/mountains");
return { mountains };
},
};
</script>
And the component with the code and prop mountains...
<template>
<div>
<ul>
<li v-for="(mountain, index) of mountains" :key="index">
{{ mountain.title }}
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['mountains'],
};
</script>
If you really want to make the API call in the child component you can use the fetch method.
Also you should not define a data() property on the page. I believe it will overwrite the server rendered data.
According to official docs :
Components in this directory will not have access to asyncData.
It means that any components inside the components folder cannot access that method.

VueJS: Not printing data returned in method

I'm successfully getting data into the console. When I try to print that data to the page by calling the method in double moustache braces it doesn't appear on screen. All other data in template appears just fine.
Template:
<template>
<div>
<div v-for="data in imageData" :key="data.id">
<div class="card">
<img :src="data.source" :alt="data.caption" class="card-img" />
<div class="text-box">
<p>{{ moment(data.timestamp.toDate()).format("MMM Do YYYY") }}</p>
<p>{{ data.caption }}</p>
// The Geocoding method is the problem
<p>{{reverseGeocode(data.location.df, data.location.wf)}}</p>
</div>
</div>
</div>
</div>
</template>
Method:
methods: {
reverseGeocode: (lat, long) => {
fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${long}&key=API_KEY&result_type=locality`
).then((res) =>
res.json().then((data) => {
console.log(data.results[0].formatted_address); // works fine
return data.results[0].formatted_address;
})
);
},
},
Here's the image data I'm getting in props
Your problem is a common problem when you start making requests in JavaScript.
The date requests are asynchronous so the method cannot return a value after the execution of the method has finished.
Imagine the following call stack:
Start method.
Throw fetch. <- Asynchronous
Finish method.
Fetch ends.
You are trying to do a return in step 4 and it should be in 3.
To solve this you should use async with await. You could also solve it by making a component and passing the data (this is my favorite since you are using vue).
Component parent
<template>
<div>
<component-card v-for="data in imageData" :key="data.id" :dataItem="data">
</component-card>
</div>
</template>
Child component
<template>
<div class="card">
<img :src="dataItem.source" :alt="dataItem.caption" class="card-img" />
<div class="text-box">
<p>{{ moment(dataItem.timestamp.toDate()).format("MMM Do YYYY") }}</p>
<p>{{ dataItem.caption }}</p>
<p>{{formattedAddress}}</p>
</div>
</div>
</template>
<script>
export default {
props: {
dataItem: {
type: {},
default: () => ({})
}
},
data() {
return {
formattedAddress: ""
};
},
created() {
this.reverseGeocode(this.dataItem.location.df, dataItem.location.wf)
},
methods: {
reverseGeocode(lat, long) {
fetch(
`https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${long}&key=API_KEY&result_type=locality`
).then(res =>
res.json().then(data => {
console.log(data.results[0].formatted_address); // works fine
this.formattedAddress = data.results[0].formatted_address;
})
);
}
}
};
</script>
I have not tried it, surely some things are missing but the template should be that.
The above I think is correct as well, but I would push for async
async reverseGeocode(lat, long) {
const response = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${long}&key=API_KEY&result_type=locality`
);
const data = response.json();
return data.results[0].formatted_address;
}
You should change your approach to the following:
Do all requests in the created() lifecycle method and store the results in a data attribute then iterate over the data attribute. The created() lifecycle method executes before the DOM is mounted so all data fetching APIs should be called there. FYR: https://v2.vuejs.org/v2/guide/instance.html
Please also refer to Vue.js - Which component lifecycle should be used for fetching data?