Creating an Angular2 Image Gallery

In order to make this tutorial easier for beginners I’m going to start by creating the simplest possible gallery, from there I’m going to develop and add more functionalities to it, which will increasingly make the gallery more complex as we proceed. If you are already familiar and have some experience with angular2 this entire tutorial will be easy for you, but if you’re a beginner it’s really important that you understand every detail from one step before you move on to the next, with that in mind let’s get started!

The first step, of course, is to create an angular2 project, if you don’t know how to create one just follow the instructions from the Angular2 Quickstart.

Before we begin let’s add the bootstrap and jquery scripts to our project, we’re going to need them later, just copy/paste the following code to your index.html.

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js" ></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" ></link>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" ></link>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" ></script>

Now we can start working on the gallery component, as I said before let’s focus on the basics first, for now all we need is a gallery that shows a list of thumbnails and a dialog with the selected image. This is how it should look like in code:

gallery.component.ts

import {Component, Input} from '@angular/core';
 
@Component({
  selector: 'gallery',
  template: `
	<div class="modal fade" id="selectedImageModal" >
	  <div class="modal-dialog" role="document">
	    <div class="modal-content">
	      <div class="modal-body">
	         <img *ngIf="selectedImage" src="{{selectedImage.url}}" >
	      </div>
	    </div>
	  </div>
	</div>
  	<ul id="thumbnailsList">
  	   <li *ngFor="let image of datasource" >
  	      <img src="{{image.url}}" class="tn"
  		  width="191" height="146"  
  		  data-toggle="modal" data-target="#selectedImageModal"
                  (click)=setSelectedImage(image)>
  	   </li>
  	</ul>
  `,
  styles: [`
  	ul { padding:0; width:780px; margin:20px auto}
  	li { display:inline;}
        .tn{ 
	   margin:2px 0px;
	   box-shadow:#999 1px 1px 3px 1px; 
	   cursor: pointer 
        }
  	.modal-content {
	    width: 670px !important;
	}
  `]
})
export class GalleryComponent { 
 
   @Input() datasource;
   selectedImage;
 
   setSelectedImage(image){
      this.selectedImage= image;	
   }
}

Look how simple the template is, we have just two elements: the selectedImageModal and the thumbnailsList. The first one is a Bootstrap dialog with the selected image and the second is the list of thumbnails.

In the GalleryComponent class we have a variable called datasource that provides everything we need to render the component, it is an array of objects containing all the data regarding each image (url, caption, title, etc), all we have to do is to iterate over this dasaset creating an img element for each object on the array, the result will be our thumbnails list.

A really important detail is that the variable datasource is decorated with @Input(), which means that it must be passed to our component as an attribute (I’m going to show how to do that in a moment), this will allow us to reuse the component to show different sets of images across the application.

We also have the selectedImage variable, as the name suggests this variable holds the object with the selected image properties. To select an image the user must click on its thumbnail, when the click occurs the method setCurrentImage will be called thanks to the (click)=setSelectedImage(image) we’ve put on the thumbnails, then a bootstrap dialog will pop up showing the full-sized selected image.

Lastly, I had to do some styling to make this component look like a gallery, one way to use CSS in an angular2 component is to just add the attribute styles to the @Component decorator like I did in this example.

Now we already have a basic gallery, let’s see how we can use it:

app.component.ts

import {Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
    <gallery [datasource]=images></gallery>
  `,
})
export class AppComponent { 
   images;
 
   constructor(){
      this.images = [
	{"url":"http://your_image1_url"},
	{"url":"http://your_image2_url"},
	{"url":"http://your_image3_url"},
	{"url":"http://your_image4_url"},
	{"url":"http://your_image5_url"},
	{"url":"http://your_image6_url"},
	{"url":"http://your_image7_url"},
	{"url":"http://your_image8_url"},
	{"url":"http://your_image9_url"},
	{"url":"http://your_image10_url"},
	{"url":"http://your_image11_url"},
	{"url":"http://your_image12_url"}
      ];
   }
}

The usage is very simple, the only thing we have to be concerned here is to pass a datasource to the gallery, in this class I have the images variable that will serve as a datasource, I’m passing it by adding the [datasource]=images to the gallery element on the template.

This is the result so far:

Angular2 Image Gallery - Thumbnails
Angular2 Image Gallery – Thumbnails
Angular2 Image Gallery – Selected Image
Angular2 Image Gallery – Selected Image

Adding the Title and Caption

As I just explained, we have to pass to the gallery component an array of objects containing all the data regarding the images, in our case the variable images from the app.component.ts is our datasource, let’s take a closer look at one of the objects that composes it:

{"url":"http://your_image1_url"}

Since the url is the only property we’ve used so far, it’s the only attribute in the object. To be able to show the title and caption we have to modify the datasource by adding these two properties to the objects, this is how it should look like after the modification:

{
  "url":"http://your_image1_url",
  "title":"Aliquam erat volutpat",
  "caption":"imperdiet imperdiet. Nullam ut ligula vitae arcu vulputate dictum ut quis elit."
}

Now we can use these new attributes on the gallery.component.ts, when a user clicks on the thumbnail we’re currently showing the selected image with this img element:

<img *ngIf="selectedImage" src="{{selectedImage.url}}" >

Let’s replace the img with a div that contains, besides the image, also the title and caption:

<div class="selectedImage" *ngIf="selectedImage">
   <img src="{{selectedImage.url}}" >
   <div class="caption">
      <p><strong>{{selectedImage.title}}</strong></p>
      <p>{{selectedImage.caption}}</p>
   </div>
</div>

And once again we have to add some styles to the component:

.selectedImage{ 
	width:640px;
	position:relative }
.caption{
	position:absolute;
        height:70px;
	width:100%;
	top:410;
	left:0;
	opacity:0.9;
	background-color:black;
	color:white;
	padding:5px;
	font-family:verdana;
	font-size:12px;
}
p {
   -webkit-margin-before: 5px !important;
   -webkit-margin-after: 5px !important;
}

This is the result:

Image Title and Caption
Image Title and Caption

Adding the Navigation Arrows

After running your application you should’ve noticed that when you select an image there’s no way to select another without closing the dialog first, we can make things much easier for the user by adding navigation arrows, for that to be possible we have to modify the template again by adding two new divs above the caption:

<div class="selectedImage" *ngIf="selectedImage">
   <img src="{{selectedImage.url}}" >
   <div class="arrow-back" (click)=navigate(false)>
      &lt;
   </div>
   <div class="arrow-forward" (click)=navigate(true)>
      &gt;
   </div>
   <div class="caption">
      <p><strong>{{selectedImage.title}}</strong></p>
      <p>{{selectedImage.caption}}</p>
   </div>
</div>

The new divs will serve as the navigation arrows, notice that they are calling the function navigate, that’s the function responsible for changing the selected image, let’s see how it works:

navigate(forward){
   var index = this.datasource.indexOf(this.selectedImage)+(forward ? 1: -1);
   if(index >= 0 && index < this.datasource.length){
      this.selectedImage = this.datasource[index];	
   }
}

The function receives a boolean as parameter, its value will be false if we click on back and true if we click on the forward arrow. To be able to change the selected image we first have to find out its index, we can easily do that by using indexOf, then we add/subtract 1 from this value and we already have a new index, now we can use this index to get the next/previous image from the datasource and assign it to the selectedImage variable.

Lastly, let’s take care of the styles:

.btn-back, .btn-forward{
	position:absolute;
	opacity:0.9;
	background-color:black;
	padding:10px;
	top:190;
	color:white;
	text-weight:bold;
	cursor:pointer;
}
 
.btn-forward{
	left:612;
}

This is the result:

Navigation Arrows
Navigation Arrows

Adding Keyboard Shortcuts

I’m not going to go into details about this because I’ve already written a post about keyboard shortcuts, you basically have to add the element host to your component:

host: {'(window:keydown)': 'hotkeys($event)'}

This allows us to call a function on the keydown event, in this case I’m calling the function hotkeys:

hotkeys(event){
   if(this.selectedImage){
      if (event.keyCode == 37){
         this.navigate(false);
      }else if(event.keyCode == 39){
         this.navigate(true);
      }
   }
}

In this function I’m just checking the keycode and taking actions accordingly, if the user presses the left arrow (keycode 37) I’m calling navigate(false) and if he presses the right arrow (keycode 38) I’m calling navigate(true).

That’s it guys! I’m going to leave it like that for now, if there are other functionalities you want to add please leave a comment, all suggestions are welcome!