1.3 Axios, components and props - VVJS/intro GitHub Wiki
If you think you have messed something up, or are joining in now, checkout tag 1.3:
git checkout 1.3 --force
Let's being by adding a script section to the Home.vue file, which can be found in /src/views/
. Usually, it would go between the template
and style
sections, but it's up to you.
<script>
import axios from 'axios'
export default {
name: 'App'
}
</script>
Axios provides a simple interface for making HTTP requests. You can think of it as By importing it in our Home component (import axios from 'axios'
), we can access axios anywhere in the script tag.
Let's add a mounted
method to start hitting the API. The mounted
method is called after the vue component is loaded into the DOM, and the component is available as this.$el
. We will use mounted
for simplicity's sake, but if you would like to see the rest of the lifecycle hooks you can find them here.
Our new asynchronous mounted
method will be placed after the data method in our component. We will be using this hardcoded URL for now, but in a real app we might get the users location and use that to find the sport places around them. Edit your scripts section so it looks like this:
<script>
import axios from 'axios'
export default {
name: 'App',
data() {
return {
sportPlaces: []
}
},
async mounted() {
const { data } = await axios.get(
'https://sportplaces-api.herokuapp.com/api/v1/places?radius=99&origin=-73.5826985,45.5119864'
)
console.log(data)
}
}
</script>
Load up your view, and open the developer tools window in the browser. If you are using chrome, with the vue app page focussed, press F12
or right click and select Inspect Element
or Inspect
. Once you have that open, click on the Console
tab at the top.
Now save your vue component and refresh your page. If all is working correctly, we should see some results.
As you may have noticed, we'd added sportPlaces
to our data method previously. Now let's replace console.log(data)
with this.sportPlaces = data.data.features
and we will have the sportPlaces
available in the view.
async mounted() {
const { data } = await axios.get(
'https://sportplaces-api.herokuapp.com/api/v1/places?radius=99&origin=-73.5826985,45.5119864'
)
this.sportPlaces = data.data.features;
}
Let's iterate over them using the v-for
directive we learned earlier.
Replace the the v-layout
element with:
<v-layout row wrap>
<v-flex xs12 md6 v-for="place in sportPlaces.slice(0, 20)" :key="place.properties.uuid">
<v-list-tile avatar>
<v-list-tile-avatar>
<img :src="getPhotoUrl(place)">
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title class="capitalize title">
{{ place.properties.name }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-flex>
</v-layout>
Once you save this, you will notice an error in your log.
vue.runtime.esm.js?2b0e:587 [Vue warn]: Property or method "getPhotoUrl" is not
defined on the instance but referenced during render. Make sure that this
property is reactive, either in the data option, or for class-based components,
by initializing the property. See:
https://vuejs.org/v2/guide/eactivity.html#Declaring-Reactive-Properties.
found in
---> <App> at src/views/Home.vue
<VContent>
<VApp>
<App> at src/App.vue
<Root>
This is because we have not created the getPhotoUrl
method yet. Before doing this, let's create a new component for our place items.
Make a new file in the /src/components/
directory called ListItemPlace.vue. For the template, copy over the v-flex
element, but leave the v-for
behind. It should look like this:
<template>
<v-flex class="px-1" xs12 md6>
<v-list-tile avatar>
<v-list-tile-avatar>
<img :src="getPhotoUrl(place)">
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title class="capitalize title">
{{ place.properties.name }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-flex>
</template>
In the script section, we will introduce a new property called props. This is an array of strings which we can use to pass data into our new component from a parent. You can learn more about props here.
<script>
export default {
name: 'PlaceListItem',
props: ['place'],
methods: {
getPhotoUrl(place) {
return `https://s3-us-west-2.amazonaws.com/meetups.hc/VVJS/intro/${place.properties.google_place_id}.jpg`
}
}
}
</script>
The getPhotoUrl
method is a simple method to extract the photo from the google maps photo reference. There is also a backup picture, in case the API does not have a picture for the given place.
Let's also slap a bit of style at the end of the vue file there:
<style scoped>
/deep/ .list__tile {
border-radius: 5px;
border: 1px solid #dfdfdf;
margin-bottom: 8px;
}
/deep/ .list__tile:hover {
background-color: #0082c385;
}
</style>
Now to display this new component in our Home view, we must first import it. Back in Home.vue, add the new import after the axios one:
...
<script>
import axios from 'axios'
import listItemPlace from '@/components/ListItemPlace.vue'
...
and add the new components property to your component:
async mounted() {
...
},
components: {
listItemPlace
}
}
Remove the old style tag from Home.vue, and we can now use our new component in the template section. Replace the inside of the v-layout
element with:
<list-item-place :place="place" v-for="place in sportPlaces.slice(0, 20)"
:key="place.properties.uuid"></list-item-place>
Save it and check out your page! It's coming along now. You should get something like this:
Sanity check: When you are all done, you should have something like this:
Home.vue
<template>
<v-container fluid>
<v-slide-y-transition mode="out-in">
<v-layout row wrap>
<list-item-place :place="place" v-for="place in sportPlaces.slice(0, 20)"
:key="place.properties.uuid"></list-item-place>
</v-layout>
</v-slide-y-transition>
</v-container>
</template>
<script>
import axios from 'axios'
import listItemPlace from '@/components/ListItemPlace.vue'
export default {
name: 'App',
data() {
return {
sportPlaces: []
}
},
async mounted() {
const { data } = await axios.get(
'https://sportplaces-api.herokuapp.com/api/v1/places?radius=99&origin=-73.5826985,45.5119864'
)
this.sportPlaces = data.data.features
},
components: {
listItemPlace
}
}
</script>
ListItemPlace.vue
<template>
<v-flex class="px-1" xs12 md6>
<v-list-tile avatar>
<v-list-tile-avatar>
<img :src="getPhotoUrl(place)">
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title class="capitalize title">
{{ place.properties.name }}
</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</v-flex>
</template>
<script>
export default {
name: 'PlaceListItem',
props: ['place'],
methods: {
getPhotoUrl(place) {
return `https://s3-us-west-2.amazonaws.com/meetups.hc/VVJS/intro/${place.properties.google_place_id}.jpg`
}
}
}
</script>
<style scoped>
/deep/ .list__tile {
border-radius: 5px;
border: 1px solid #dfdfdf;
margin-bottom: 8px;
}
/deep/ .list__tile:hover {
background-color: #0082c385;
}
</style>