I recently finished my final project for Learn Verified! It was an exciting moment, a culmination of everything I’ve learned in these past four months. My favorite moment came when I realized how to connect my rails database to the front end, which was built with Angular. Below you’ll find the steps with lots of code snippets. Peruse the full code here.

This article is quite technical and assumes you have working knowledge of rails and angular.

Connecting the Back End and Front End

A struggle I first had in building the front end of apps with Angular was that I couldn’t use rails helpers in my views. The front end and back end aren’t automagically connected anymore! To get the information from the database onto the front end, I had to serialize it and use $http.get to grab and render JSON data with Angular.

The most interconnected part of this application is a feature which grabs a user’s data from Spotify and then renders a chart of the artist’s earnings on the front end (check out the whole project here). Here’s a breakdown of the process, in eleven easy steps:

angular chart of artists

Rails Back End

Here’s the steps of the rails part of the app.

First:

A user authenticates the Spotify API after clicking ‘Log Into Spotify’, which refers them to /users/auth/spotify

Second:

Oauth handles the Authorization Code Flow and returns a secure key to the backend, also logging a user into Devise or creating their account with their Spotify email.

# app/controllers/users
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def spotify
    @user = User.from_omniauth(request.env["omniauth.auth"]
    ...
  end
end

# in models/user.rb
class User < ActiveRecord::Base
...
def self.from_omniauth(auth) # grab data for user from Omniauth.
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    user.name = auth.info.name
    user.email = auth.info.email
    user.password = Devise.friendly_token[0,20]
    user.access_token = auth.credentials.token
    user.refresh_token = auth.credentials.refresh_token
    end
  end

Third:

The RSpotify gem (which is amazing) grabs the user’s top twenty artists, which I sliced to be only the top five artists.

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
...
  spotify_user = RSpotify::User.new(request.env['omniauth.auth'])
  top = spotify_user.top_artists.slice(0,5)
  Artist.parse_from_user(@user, top)
  Genre.scrape_genres(top)
  end
end

Fourth:

The models extract the artist and genre data, saving it to the database and associating it with the user.

# Similar logic is used for the genres in the genre model
class Artist < ActiveRecord::Base
  def self.parse_from_user(user, array)
    array.each do |artist|
      newbie = Artist.find_or_create_by(name: artist.name)
      newbie.name = artist.name
      newbie.image = artist.images[0]["url"]
      newbie.popularity = artist.popularity
      newbie.link = artist.external_urls["spotify"]
      newbie.uri = artist.uri
      newbie.followers = artist.followers["total"]
      user.artists << newbie unless user.artists.include?(newbie)
      newbie.save
      user.save
    end
  end
end

Fifth:

Active Model Serializer and controllers render the JSON for each RESTful URL.

# in app/serializers/user_serialzer.rb
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name
  has_many :artists
end

Which renders JSON at /user/:id (don’t forget to set the show method in the users controller and routes!)

{
    "id": 1,
    "name": "lukeymoo",
    "artists": [
    {
        "id": 1,
        "name": "Teebs",
        "image": "https://i.scdn.co/image/787867c011ffcb68f684378c5bdbc7004e71cb55",
        "streams": null,
        "link": "https://open.spotify.com/artist/2L2unNFaPbDxjg3NqzpqhJ",
        "followers": null,
        "uri": "spotify:artist:2L2unNFaPbDxjg3NqzpqhJ"
    }, ...

Sixth:

After all this data is processed, the user is redirected to the #/user/:id page, where we pick up the front end. This required an override of Devise’s after_sign_in_path_for method.

class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
    id = resource.id.to_s
    '/#/chart/' + id || root_path
  end
end

The Angular Front End

Seventh:

The ui-router for a user has a dynamic URL for each user, which is #/user/:id.

angular // dependencies are ui.router and chart.js, among others
  .module('app', [
    'ui.router',
    'chart.js',
    ])
    ...
    .state('userChart', {
        url: '/chart/:id',
        templateUrl: 'userChart.html',
        controller: 'UserChartController as user',
      })

Eighth:

The UserChartController grabs the user ID from $stateParams, so it can call the right user’s data. This is almost identical to params[:id] in Rails.

function UserChartController($scope, $stateParams, BackEndService, LastfmService) {
var userId = $stateParams.id

BackEndService /
    .getUserArtists(userId)
    .then(function(response) {
    ... // what to do with this data
    })
}

Ninth:

The BackEndService calls to the /user/:id and grabs the JSON data for that user, which is all their top artists. Here is the point of connection! This returns the JSON data mentioned above in step five.

function BackEndService($http) {
...
  this.getUserArtists = function(id) {
    return $http.get('http://localhost:3000/users/' + id)
  }
}

And, finally, tenth!

The data that get returned are put into an array of names. For each artist, I call the Last.fm API to grab the number of total listens.

This is passed to a Chart.js chart, which renders the data on the front page! Which is as easy as:

function UserChartController($scope, $stateParams, BackEndService, LastfmService) {
...
$scope.labels = names; // names is an array of artist names pulled from the user data
$scope.data = [listens];  //listens is an array of the number of listens.

Because Angular-Chart-JS is so rad, creating that chart is done with calling a canvas object in the view.

See the full code of the UserCharts controller here. Or read about this entire final project here.