Using Vue Template Syntax to Build A Photo Gallery

Parthiv Mohan

Vue is best used when with its templating features. It becomes very intuitive to build fancy user interfaces.

Take its directives, for example, which refers to tag attributes with the v- prefix.

You could have a variable url in your Vue instance that your anchor tag uses as its href. That would look like this:

<a v-bind:href="url"></a>

Let’s try it with the other directive that we’ll find ourselves using a lot:

<a v-on:click="myFunction"></a>

That is how we would call one of our component’s functions upon clicking the link.


Dynamic arguments take your directives to a new level. Consider the following:

<a v-bind:[attributeName]="url">...</a>

attributeName is itself a JavaScript expression like url, interpreted as such because of the square brackets around it.

<a v-on:[event]="myFunction"></a> would mean that the event variable could be "hover" or "click" or any other attribute used with v-on.


Let’s go over one more thing.

The directives v-on and v-bind are so common that we have shortcuts for writing them in Vue; : and @.

So, an img tag with a dynamic attribute could be <img :[classOrId]="value" @click="display"> where display is a function, value is a string variable, and classOrId is also a string variable.


To expand on that, we’re going to create a photo gallery with some of this new fangled template syntax. Get ready!

App Setup

If you don’t have the Vue CLI installed already, start by running either of these commands in your terminal, depending on if you prefer using npm or Yarn:

$ npm install -g @vue/cli

or

$ yarn global add @vue/cli

Now you’ll be able to run the vue command from the command line. Let’s create a Vue app called alligator-zest

$ vue create alligator-zest
$ cd alligator-zest
$ npm run build
$ npm run serve

Next, we’re going to change HelloWorld.vue to be PhotoGallery.vue. App.vue should look something like this:

App.vue

<template>
  <div id="app">
    <PhotoGallery/>
  </div>
</template>

<script>
import PhotoGallery from './components/PhotoGallery.vue'

export default {
  name: 'App',
  components: {
    PhotoGallery
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

PhotoGallery.vue is where we’re about to get fancy while keeping things simple at the same time. 🌈

Let’s assume we have 5 photo files in the assets/photos folder named 1.jpeg through 5.jpeg. Use any images you want.

PhotoGallery.vue

<template>
  <div>
    <ul class="gallery">
      <li v-for="n in 5" :key="n">
        <img
          :src="require('@/assets/photos/' + n + '.jpeg')"
        >
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'PhotoGallery'
}
</script>

<style scoped>
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
.gallery {
  display: flex;
  justify-content: space-around;
}
img {
  width: 20%;
}
</style>

The @ symbol is a webpack alias that points to the src folder.

Note the display: flex on "gallery" as well as the v-for in the <li> tag. You should be able to see the app in your browser at localhost:8080.

Let’s update this code so that when we click on a photo it’s enlarged.

PhotoGallery.vue

<template>
  <div>
    <ul class="gallery">
      <li v-for="n in 5" :key="n">
        <img
       	  @click="highlight"
          :src="require('@/assets/photos/beijing/' + n + '.jpeg')"
        >
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'PhotoGallery'
},
methods: {
  highlight() {
    event.target.id = "theater";
    let eventIterator = event.target.parentNode;
    while (eventIterator.previousElementSibling != null) {
      eventIterator.previousElementSibling.getElementsByTagName('img')[0].id = "";
      eventIterator = eventIterator.previousElementSibling;
    }
    eventIterator = event.target.parentNode;
    while (eventIterator.nextElementSibling != null) {
      eventIterator.nextElementSibling.getElementsByTagName('img')[0].id = "";
      eventIterator = eventIterator.nextElementSibling;
    }
  }
}
</script>

<style scoped>
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
.gallery {
  display: flex;
  justify-content: space-around;
}
img {
  width: 20%;
}
#theater {
  width: 40%;
}
</style>

We added a v-on:click to each image that sets off the highlight() method. This method makes the image that is clicked-on become larger while ensuring that the others are thumbnail-sized.

It sets the id of the clicked image to "theater" which has a larger width. Then, it gets the sibling nodes of the parent node off of the image, the li with the v-for in it. It goes into all of these li tags and sets their respective img tag's id to a null string to make sure that only one img has the "theater" id at any given time.

This is cool but it’s still not what we see on the web; how can we get the enlarged image to be a big display, say, under the 5 thumbnails? The end result would be a roll of thumbnails with the selected image enlarged to a real theater size right below. Sounds good, right?

We’re going to add the following:

<div id="frame">
  <img
    :src="this.theatrical"
  >
</div>
data() {
  return {
    theatrical: ""
  }
},
methods: {
  highlight() {
    event.target.id = "theater";
    this.theatrical = event.target.src;

And finally, add the following to your CSS.

#frame img {
  width: 80%;
}

Check it out in your browser!

The big frame is filled up by the photo that you clicked-on since its src attribute gets set when you click. Now you have a nice gallery view of your photos!

All with some nifty use of Vue’s reactivity system, data properties, and template syntax. 🧪

  Tweet It

🕵 Search Results

🔎 Searching...