Simple Photo App with Vue.js, Axios and Flickr API — Part 1
Project setup and our first API call
Hello! Welcome to Part 1 of this tutorial series, in which we’ll create a simple photo app using Vue.js, Axios, and the Flickr API. The goal of this series is to practice basic Vue concepts and gain exposure to working with an API. I am assuming basic working knowledge of HTML, CSS, and Javascript, and some familiarity with Vue. If you’re new to the latter, I suggest checking out a different series I wrote, Creating an Online Store with Vue CLI, where I offer more detailed explanations of core concepts. Let’s get to it!
Step 1: Project setup
Assuming you have the latest version of the Vue CLI installed, create a new project by navigating to where you want to save it in your terminal and entering the following:
vue create vue-flickr
We will manually select the following features:
? Check the features needed for your project:
◉ Babel
◯ TypeScript
◯ Progressive Web App (PWA) Support
◉ Router
◯ Vuex
◯ CSS Pre-processors
◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) Y? Pick a linter / formatter config:
ESLint with error prevention only
❯ ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ Lint on save
◯ Lint and fix on commit? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.?
In dedicated config files
❯ In package.json? Save this as a preset for future projects? (y/N) Nsuccess Saved lockfile.✨ Done in 5.46s.
⚓ Running completion hooks...
📄 Generating README.md...🎉 Successfully created project vue-flickr.
👉 Get started with the following commands:$ cd vue-flickr
$ yarn serve
cd
into vue-flickr
and run yarn serve
(or npm run dev
) to confirm the project was successfully created. You will see the following:
Step 2: Additional package installation
In order to make requests to Flickr, we need a popular library called Axios. With Axios we’ll be able to:
- Make XMLHttpRequests from the browser
- Make http requests from node.js
- Support the Promise API
- Intercept request and response
- Transform request and response data
- Cancel requests
- Automatically transform data into for JSON
- Have client side protection against XSRF
Don’t worry if you don’t know what all that means. Just know axios
helps us talk to Flickr. Install it by running the following in your terminal:
npm install axios --save
The installation was successful if you see this:
+ axios@0.18.0added 90 packages from 123 contributors, removed 80 packages, updated 1082 packages and audited 24519 packages in 57.923sfound 0 vulnerabilities
Step 3: API Setup
Working with an API requires something called a key
, a unique identifier that let’s Flickr know we’re a legitimate source making a request. To get that key, you have to have a Flickr account. And to get a Flickr account, you have to have a Yahoo email account. So:
- Create a Yahoo account if you don’t have one
- Log into Flickr
- Visit Flickr API Services and click “Create an App”
4. Click “Request an API Key” on the next page.
5. Click “Apply for a non-commercial key”.
6. Enter a name and description for your app. Check the boxes and hit “Submit”.
7. You’ll then be taken to a page where you can copy the key
provided.
8. In the root project directory, (at the same level that index.html
is located) create a file called config.js
and add the following contents:
export default {
api_key: YOUR_KEY_HERE
}
Step 4: Making our first API call
We now have all the pieces to fetch photos from Flickr! Next, empty out Home.vue
:
We need a way to enter and submit a search term. Add a form to the template that contains one text input and a submit button. The text input should usev-model
to connect to a tag
data property. The submit button should fire a search
method when clicked, BUT it should not reload the page (hence the prevent
modifier on the click
event).
Make sure to actually add the data()
function for the tag
property and the methods
section with the search
function. For now the search
function can just log this.tag
to the console:
After a user submits their search term, the page should populate with photos that have been tagged with that term. The template should display a loading...
message while the data is being fetched and then include an unordered list of photos (using v-for
) once it’s ready.
Based on the above, our template is now using two new data properties, loading
and images
, so be sure to add them to the data()
function.
Next, our search
method needs to do a couple of things. When it’s first called, it should change loading
to true
, since the data fetching process is underway. Then it should make a request to the Flickr API using axios
, and when the response (image data) is available, populate images
with an array of images and set loading
to false
.
Let’s break the axios
/API call part down a bit more. For clarity I separated out the actual API call into its own method, fetchImages()
. To use axios
, I imported it at the top of the script
section, along with config
, where our api_key
is stored (because we’ll need that momentarily as well). axios
then needs an object that specifies what kind of request we’re making (the method
), where we’re making a request to (the url
), and a bunch of parameters
that further specify what kind of information we want from the url
. The params
object has the following contents:
method
— we use theflickr.photos.search
method to perform a searchapi_key
— we pass in ourapi_key
so Flickr trusts ustags
— the terms we want to search for, provided by ourtag
data propertyextras
— any additional info we want about the photos that get returned. We want to see the photo so we need theurl_n
string.owner_name
tell us who took the photo.datetaken
tells us when the photo was taken. Andviews
tell us how many people have viewed the photo.
You can find a full list of what’s available for a given photo here, and you can play around with the flickr.photos.search
method here.
The rest of the params
specify how many photos we get back and what kind of format the data is presented to us in. json
will allow us to easily navigate the data structure in Javascript.
fetchImages()
handles that actual retrieval of the photos, but it doesn’t populate images
. That happens in a subsequent callback once the data is ready. We know the data is ready when a Promise is returned. Promises are what allow us to retrieve data asynchronously. In other words, we can ask axios
to work on getting the photo data for us while also letting Vue do other things. In short, Promises help our app run faster and perform multiple tasks at once.
When the Promise is returned with our data, we tell Vue what to do with it. The then()
method is called when a Promise is returned successfully. It accepts the response
, which contains a data
object. Inside data
, we’ll find a photos
object, which contains an array of photos named photo
. That’s what we want images
to store. Once we do that, we can mark loading
as false and reset our tag
property.
.then((response) => {
this.images = response.data.photos.photo;
this.loading = false;
this.tag = "";
})
After all this, you should get similar output to the following:
Step 5: Make an ImageCard Component
Yay! We’ve got access to data. Now let’s make it human readable by updating our template. Inside the unordered list of Home.vue
, add:
...
<li v-for="image in images" :key="image.id">
<img :src="image.url_n" :alt="image.title">
<div>
<p v-if="image.title">{{image.title}}</p>
<p v-else>No Title Found</p>
<p>By {{image.ownername}}</p>
<section>
<p>{{image.datetaken}}</p>
<p>Views: {{image.views}}</p>
</section>
</div>
</li>
...
That’s better, but the page still needs some styling. To simplify Home.vue
, pull the above markup into its own component. Inside the components
directory, delete HelloWorld.vue
and make a file called ImageCard.vue
. Place the following inside:
The above component has the same markup from Home.vue
, and receives an image
prop so it knows what data to render. To use the ImageCard
component, import it in Home.vue
and register it:
...
import ImageCard from '@/components/ImageCard';
export default {
name: 'home',
components: {
ImageCard
},
...
}
Then add it to the template in place of the previous <li>
markup:
...
<ul v-else>
<image-card
v-for="image in images"
:key="image.id"
:image="image" />
</ul>
....
The result should be the same as before in your browser.
Now add styles to Home.vue
and ImageCard.vue
to make it pretty.
During the project setup I didn’t install a pre-processor, so run the following in order to use SCSS:
npm install -D sass-loader node-sass
Update ImageCard.vue
with class names in the template and a styles section:
Home.vue
also needs some love. Style the navbar to be full-width but leave the list of photos is still centered with some margins. I also made a few other structural template modifications and of course added new class styles:
One last part here, I made a few CSS changes to App.vue
as well:
The output should then look like this:
Step 6: Formatting dates with Moment.js
Our cards are looking great, but the dates are a little ugly. Moment.js is a popular date formatting library we can use. Install it with:
npm install moment --save
Inside ImageCard.vue
, import moment
and create a filter
to format the date. Think of filters as functions that receive an input in need of formatting. The filter returns output in a way that we want. Visit the string formatting page for more ways to control how the dates show up:
<script>
import moment from 'moment';export default {
name: 'ImageCard',
props: [ 'image' ],
filters: {
moment(date) {
return moment(date).format("MMMM Do, YYYY");
}
}
}
</script>
Use the filter in the template by adding the pipe character after the date we want to format, and referencing our filter by its name:
<p class="image-date">{{image.datetaken | moment}}</p>
Step 7: Basic data cleaning
You might’ve noticed that sometimes images don’t show up, producing the following in your browser:
This is probably due to data inconsistency from Flickr (ie a missing url_n
string). A simple solution is to create a computed property called cleanImages
that filters out any image that doesn’t have a url_n
, and use cleanImages
to loop through in our template instead of images
.
In Home.vue
, add the computed property:
...
computed: {
cleanImages() {
return this.images.filter(image => image.url_n)
}
},
Then in the template, change images
to cleanImages
:
<template>
...
<image-card
v-for="image in cleanImages"
:key="image.id"
:image="image" />
...
</template>
And there you have it! We’ve used axios
and Vue.js to make a simple get
request to the Flickr API, and made it look nice with basic CSS styles and some help from Moment.js! Next up, in Part 2, we’ll add image detail pages, and refactor our app a bit from a routing/page standpoint. See you then! 👋