Build a Mini Instagram App with Aurelia

Aurelia is yet another component based JavaScript framework (not just library). It is an interesting one because of its simplicity and the use of futuristic tools like ES2016. This is a getting started guide for Aurelia and by the end you will know the basics of Aurelia and why it's worth a shot.

Seriously!!? Why Another One?

Yeah! I get it. If you are like me, you must have started with Angular 1 which we all loved. React was introduced and because it is component based, therefore, the future, you had to learn it. Half way into your React journey, Angular 2 was released and you can't afford to miss it because every JS developer loves angular including you. Now you have 2 heavy tools to be asking questions about on Stack Overflow which we all know how that hurdle is difficult. And here I am telling you about another one.... what's the name again, yeah Aurelia. I understand how you feel and I felt same way as well but after going through the cool stuffs Aurelia is bringing to the table, I couldn't help but give it a shot. Some of this cool benefits include:

  1. Coding in vanilla JS
  2. Support for TypeScrit, ES2016 and ES2015 (I am not kidding)
  3. Simple and intuitive all-way data binding
  4. Awesome routing with less to no drama
  5. Frictionless Setup
  6. Testing as a habit
  7. Complies with most of W3C standards

Let's hop in and see for ourselves.

First Component

We will make a mini Instagram app but before we build our killer IG photo app, it will be cool to have a quick look at Aurelia by creating a simple component first. You need to download the Aurelia starter kit, unzip the folder to your working directory, and rename the folder to scotch-aurelia or what suites you.

Install a Node dependent HTTP server to assist in running your app:

npm install -g http-server

Run:

 http-server -o -c-1

The browser will show up with a welcome message as seen below. That is dumb enough for a first run.

We can do better, let's explore the Aurelia world. Bearing in mind that this is a starter guide, we could skip some details and focus on primary subjects. This is what our app will hopefully look like at the end of this tutorial:

The index.html on the root is used to bootstrap the app by:

1) Using SystemJS to import aurelia-bootstrapper 2) Adding aurelia-app to the markup body

 <!DOCTYPE html>
 <html>
   <head>
     <title>Aurelia</title>
     <link rel="stylesheet" href="styles/styles.css">
     <meta name="viewport" content="width=device-width, initial-scale=1">
   </head>

   <body aurelia-app>
     <script src="jspm_packages/system.js"></script>
     <script src="config.js"></script>
     <script>
       SystemJS.import('aurelia-bootstrapper');
     </script>
   </body>
 </html>

Pretty straight forward, right? SystemJS and it's config was also imported before the bootstrap took place which makes sense.

Components in Aurelia by default are made up of a view (HTML template) and a view-model (JS class). Your project if otherwise configured will use app.js and app.html located in src folder as entry component to your app. The starter project includes that so let's update them to see how data binding works:

// src/app.js
export class App {
  // Properties
  message = 'Welcome to Aurelia!';
  firstNum = 0;
  secondNum = 0;

  // Get method that returns the sum of the two integer properties
  get sum () {
    return parseInt(this.firstNum) + parseInt(this.secondNum);
  }

  submit () {
    alert(`Sum of ${this.firstNum} and ${this.secondNum} is ${this.sum}`)
  }

}

Three properties, two methods, no much surprise if you have basic programming skills. The amazing thing here is that the properties are what we can bind to our view and we can call the methods by triggering events:

 <!--  src/app.html -->
 <template>
   <h1>${message}</h1>

   <form submit.trigger="submit()">
     <input type="text" value.bind="firstNum"> ${firstNum}
     <input type="text" value.bind="secondNum"> ${secondNum}
     <input type="submit" value="Sum">
   </form>
 </template>

The template is wrapped with template thereby playing by the rules as suggested by W3C. Binding is made with .bind and events with .trigger. There are lots of options available in the Aurelia documentation but this is basically what we do in modern apps.

Time to build something fun and explore the awesomeness of Aurelia.

Simple Instagram App with Aurelia

To better explore Aurelia, let us build a simple Instagram app together. This will give us the chance to have a look at features like routing, HTTP requests, advanced templating, etc. To prepare for that, clear the contents of app.js and app.html.

Aurelia Routing Basics

The existing app.js which I earlier mentioned is the default app's entry component makes more sense to handle route logics:

// src/app.js
export class App {
  // Implement configureRouter method
  configureRouter(config, router) {
    config.title = 'Scotch IG';
    // Use map to set array of possible routes
    config.map([
      { route: ['','home'], name: 'home', moduleId: './home', nav: true, title:'Home' },
      { route: 'me', name: 'me',  moduleId: './me',    nav: true, title:'Me' }
    ]);

    // Create a binding to the router object
    this.router = router;
  }
}

Simple as that. All you just need to configure routes in Aurelia is to Implement the configureRouter method which takes two parameters - config which is used to configure routes and router which is an object of information about the routes. We also need to tell the framework where to mount other components when using routes. This is done in app.html:

 <template>
   <!-- Require 3rd party libraries  -->
   <require from="bootstrap/css/bootstrap.css"></require>
   <require from="font-awesome/css/font-awesome.css"></require>

   <!-- Bootstrap Navigation  -->
   <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
     <div class="navbar-header">
       <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
         <span class="sr-only">Toggle Navigation</span>
         <span class="icon-bar"></span>
         <span class="icon-bar"></span>
         <span class="icon-bar"></span>
       </button>
       <a class="navbar-brand" href="#">
         <i class="fa fa-home"></i>
         <span>${router.title}</span>
       </a>
     </div>

     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
       <ul class="nav navbar-nav">
         <!-- Loop through routes to create a menu  -->
         <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
           <a href.bind="row.href">${row.title}</a>
         </li>
       </ul>

       <ul class="nav navbar-nav navbar-right">
         <!-- Show loader when changing routes  -->
         <li class="loader" if.bind="router.isNavigating">
           <i class="fa fa-spinner fa-spin fa-2x"></i>
         </li>
       </ul>
     </div>
   </nav>

   <div class="page-host">
     <!-- Route mount point  -->
     <router-view></router-view>
   </div>
 </template>

The most significant content is the mount point specified with router-view. That is where the components of each routes are loaded into when they are called upon. The weirdest thing (but actually amazing) here is that we are doing a require inside the template. Did we just load Bootstrap and FA? Yeah we did. Aurelia uses a module management system that will blow your mind. You can require anything (npm or Github) anywhere (markup or class). This is achieved by a package manager called JSPM.

JSPM is a frictionless browser package management tool for for the SystemJS universal module loader, built on top of the dynamic ES6 module loader.

You may have as well noticed that repeat is used to loop through available navigation and bind to the view while if expression is used to decisively show a spinner when changing routes. As you can see, Aurelia solves common daily routing issues seen in modern JS frameworks.

We just created two routes whose components are missing, let us flesh those out:

// src/home.js
export class Home {
  heading = "Welcome to Scotch IG";
}
<!-- src/home.html  -->
<template>
  <div class="jumbotron">
    <h2 class="text-center">${heading}</h2>
  </div>
</template>
// src/me.js
export class Me {
  heading = "Me";
}
<!-- src/me.html -->
<template>
  <div class="jumbotron">
    <h2 class="text-center">${heading}</h2>
  </div>
</template>

Now reload the browser and feel the awesomeness of Aurelia routing. Don't forget to notice the cool spinner, though it will be more useful in the next section where we explore HTTP. I know you have been waiting for that.

HTTP Requests, DI, Authentication

I don't know about you, but routing is not my favorite part in modern frameworks, Ajax is. Time to use Aurelia's futuristic fetch which is also backward compatible to talk to Instagram's API.

Instagram API can be accessed only if an access_token is provided by the requesting user. Basically, you get the user to sign in, Instagram assigns a token, then you grab that token and use it to make request on behalf of the user. Sounds like work? Auth0 to the rescue. You can decide to go hardcore but these days everybody loves the word "frictionless".

  • Register a new application on Instagram and store the CLIETN ID and CLIENT SECRET somewhere safe.

  • Setup an Auth0 account, create an app from the dashboard (I named mine Scotch IG)
  • Enable Instagram connection from the dashboard by clicking connections, social and turning on Instagram. The hopefully stored Instagram credentials will be requested. Check the permission boxes and hit Save.
  • The Apps tab will be activated, turn on the App you created, in my case Scotch IG.
  • Finally, go to the application settings and supply http://localhost:8080/#/ to the Allowed Callback URLs then copy the Auth0 App Client ID. Save your settings. Trust me you have authentication setup with Instagram already.

In order not to loose track, let us complete the rest of this section in a stepwise manner:

0) Install Dependencies

Install JSPM globally and Auth0.js locally with JSPM

# Install jspm globally
npm install -g jspm

# Install auth0-js with jspm
jspm install npm:auth0-js

1) Create an Auth Service

A simple service to initialize Auth0 and sign user into Instagram:

// src/auth-service.js

// Import Auth0
import Auth0 from 'auth0-js';

export class AuthService {
  constructor() {
    // Initialize Auth0
    this.auth0 = new Auth0({
      domain:       'YOUR AUTH0 DOMAIN',
      clientID:     'YOUR AUTH0 CLIENT ID',
      callbackURL:  'http://localhost:8080/#/',
      callbackOnLocationHash: true
    });
  }

  sigin() {
    //Keep a copy of 'original' this
    const _this = this;
    // Login with IG
    this.auth0.login({
      connection: 'instagram',
      popup: true
    },
    function(err, profile) {
      if (err) {
        alert("something went wrong: " + err.message);
        return;
      }
      // Use ID token to get Instagram user profile
      _this.auth0.getProfile(profile.idToken, function (err, profile) {
        if(err) {
          alert(err);
          return;
        }
        localStorage.setItem('token', profile.identities[0].access_token);
      });
    });
  }

  signout() {
    localStorage.removeItem('token');
  }

}

Auth0 will give you Auth0 credentials after a successful login but we can't query IG with that. That is why we request for the IG profile with auth.getProfile() even after a login.

It is important to note that we are storing in the token in localStorage to enable us access it from any part of our SPA even after a reload.

2) Authenticate User and Get Token

With the service written, we can inject it in our existing home.js and use it to authenticate a user. Note that your app is in Sandbox mode which has a lot of limitation but has enough features for this tutorial.

// src/home.js

import {inject} from 'aurelia-framework';
import {AuthService} from './auth-service';

// DI for AuthService
@inject(AuthService)
export class Home {
  heading = "Welcome to Scotch IG";

  constructor(authService){
    this.authService = authService;
  }

  sigin() {
    // Sign in
    this.authService.sigin();
  }

  signout() {
    // Sign out
    this.authService.signout();
  }
}

All that is needed to leverage Aurelia's DI feature is to decorate the class with @inject passing the injecatables

Then create a login button in the template:

<!-- src/home.html  -->
<template>
  <div class="jumbotron">
    <h2 class="text-center">${heading}</h2>
    <p class="text-center">
      <a click.trigger="sigin()" class="btn btn-primary">Login</a>
    </p>
  </div>
</template>

3) Create Instagram Service First instal Aurelia's HTTP client for making JSONP request to IG:

jspm install aurelia-http-client

The create an ig-service file for our service class:

// src/ig-service.js
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';

//DI
@inject(HttpClient)
export class IgService {
  token = localStorage.getItem('token');
  constructor(http) {
    //Configure basw URL
    http.configure(config => {
      config
        .withBaseUrl('https://api.instagram.com/v1/');
    });
    // Set http property
    this.http = http;
  }

  recent() {
    // Return a promise which when resolved will respond with recent posts
    return this.http.jsonp(`users/self/media/recent/?access_token=${this.token}`, 'callback');
  }

  me() {
    // Return a promise which when resolved will respond with user's profile
    return this.http.jsonp(`users/self/?access_token=${this.token}`, 'callback');
  }

}

4) Consume IgService in View Models

Let's consume our service in the home and me view models:

// src/home.js

import {inject} from 'aurelia-framework';
import {AuthService} from './auth-service';
import {IgService} from './ig-service';

// Browser-based implementation of Aurelia's platform abstraction layer.
// activate() will throw an error if missing
import {initialize} from 'aurelia-pal-browser';
initialize()

// DI for AuthService
@inject(AuthService, IgService)
export class Home {
  heading = "Welcome to Scotch IG";

  constructor(authService, igService){
    // Setup Dependencies
    this.authService = authService;
    this.igService = igService;
  }

  sigin() {
    // Sign in
    this.authService.sigin();
  }

  signout() {
    // Sign out
    this.authService.signout();
  }

  // Lifecycle method called when a route is activated
  activate() {
    if(localStorage.getItem('token')){
      // Resolve promise returned from igService
      return this.igService.recent()
        .then(res => res.response.data)
        .then(recent =>
          {
            // Bind to view
            this.recent = recent
          });
      }
  }
}
// src/me.js

import {inject} from 'aurelia-framework';
import {initialize} from 'aurelia-pal-browser';
import {IgService} from './ig-service';
initialize();

// DI
@inject(IgService)
export class Me {
  heading = "Me";
  me = {};

  constructor(igService){
    // initialize
    this.igService = igService;
  }

  activate() {
    if(localStorage.getItem('token')){
      // Resolve promise
      return this.igService.me()
        .then(res => res.response.data)
        .then(me =>
          {
            // Bind to view
            this.me = me
          });
      }
  }
}

5) Bind to view

Now we can use the basic binding techniques we have seen to flesh out our views:

<!-- src/home.html  -->
<template>
  <div class="jumbotron">
    <h2 class="text-center">${heading}</h2>
    <p class="text-center">
      <a click.trigger="sigin()" class="btn btn-primary">Login</a>
      <a click.trigger="signout()" class="btn btn-warning">Logout</a>
    </p>
  </div>

  <div class="container">
    <div class="col-md-4" repeat.for="post of recent">
      <div class="panel panel-default">
        <div class="panel-body">
          <img src.bind="post.images.standard_resolution.url" class="img-responsive" alt="" />
        </div>
        <div class="panel-footer"><strong>${post.caption.from.full_name} </strong> ${post.caption.text}</div>
      </div>
    </div>
  </div>
</template>
<!-- src/me.html  -->
<template>
  <div class="jumbotron">
    <h2 class="text-center">${heading}</h2>
    <p class="text-center">
      <img src.bind="me.profile_picture" alt="" style="border-radius: 100px">
    </p>
    <p class="text-center">
      <strong>Followed by: </strong> ${me.counts.followed_by};
      <strong>Following: </strong> ${me.counts.follows};
      <strong>Media: </strong> ${me.counts.media};
    </p>
  </div>
</template>

Conclusion

We have a fair introduction to Aurelia. Do not hesitate to visit the documentation as there are more fun stuff to explore. Maybe you might just end up using this framework in your next project or maybe introduce to a friend.

Chris Nwamba

Passion for instructing computers and understanding its language. Would love to remain a software engineer in my next life.