Rails and AngularJS

35
BUILDING THE ASSAY PIPELINE HTTP://ASSAYPIPELINE.COM SEAN CARROLL / ALAIN MIR Rails and AngularJS

description

Rails and AngularJS. Building the Assay Pipeline http://assaypipeline.com Sean Carroll / Alain Mir. Assay. A laboratory test used in Disease diagnosis DNA matching Drug testing Assay Design is a complex, iterative process Scientists use an array of FOSS tools - PowerPoint PPT Presentation

Transcript of Rails and AngularJS

BUILDING THE ASSAY PIPELINEHTTP: / /ASSAYPIPELINE.COMSEAN CARROLL / ALAIN MIR

Rails and AngularJS

Assay

A laboratory test used in Disease diagnosis DNA matching Drug testing

Assay Design is a complex, iterative processScientists use an array of FOSS toolsCommercial products now appearing

Assay

An assay is an investigative (analytic) procedure in laboratory medicine, pharmacology, environmental biology, continuous delivery, and molecular biology for qualitatively assessing or quantitatively measuring the presence or amount or the functional activity of a target entity (the analyte). - Wikipedia

Project Background

“Do you know about databases” ?What does it do ?Cancer tumor database ~ 1.2m entriesLarge datasetsApplication built as a Rails 3.2 application

Demo

Application Demo

UX Components

AngularJSRestangularBootstrap (CSS only)UI Bootstrap (js)UI Select 2UI SortableXeditable

Why Angular

Immersive ExperienceOrganize the JavaScript and Rails Helper

spaghettiLots of Ajax functionalityClear separation between the view and modelIt’s new-ish and coolIt’s the most popular JS framework today

Challenges

Learning curve!! Angular Javascript

Promises ! Changing technologies 1 -> 1.2 Version 2 of Angular quite different

Building two distinct applications

Architecture

Rails Model

Rails Controller

Rails Serializer

Rails Routes

Rails API JSON / HTTP

Angular Routes

Angular Controller / Model

HTML Templates

Why keep Rails

Why not go with a full stack Javascript solution? Gems Framework Stability What I already knew This application doesn’t need the real-time push of

Meteor.js

How to Integrate Angular

Option 1: Completely separate applications Build the front end with Grunt Can still live in the same Github repo

Option 2: Server Angular with the Asset Pipeline Definitely easier Need LiveReload to keep assets up to date (including

Angular.js application code) Can continue to use Rails ERB views for some parts of

the application

Rails and Angular

Turn off TurbolinksTurn off / replace Javascript ManglingCreate a API for Rails – Angular

communicationCreate Angular “View-Controllers”Create Angular RoutingCreate Angular HTML pages

Gemfile

gem 'angularjs-rails','~> 1.2.6’gem 'ngannotate-rails'

layouts/application.html.erb

Bootstrap the Angular application

<body ng-app="assaypipelineApp"><%= yield %>

</body>

layouts/application.js

//= require angular//= require angular-resource//= require angular-route//= require lib/restangular.min//= require assaypipelineApp//= require_tree .

layouts/application.js

//= require angular//= require angular-resource//= require angular-route//= require lib/restangular.min//= require assaypipelineApp//= require_tree .

javascripts/assaypipelineApp.js

var assaypipelineApp = angular.module('assaypipelineApp', [ 'ngRoute', 'ngResource',

'ui.sortable', 'ui.bootstrap', 'angularFileUpload', 'ui.select2',

'restangular', 'xeditable' ]);

javascripts/assaypipelineApp.js

assaypipelineApp.config([ '$httpProvider', function ($httpProvider) { var authToken; authToken = $('meta[name="csrf-token"]').attr('content'); $httpProvider.defaults.headers.common['X-CSRF-TOKEN'] = authToken; } ]);

Add a Rails API

Rails API

class Api::BaseController < ApplicationController before_filter :authenticate_user!

private def permission_denied render json: {error: 'unauthorized'}, status: :unauthorized endend

class Api::UsersController < Api::BaseController before_filter :verify_is_admin

def index render json: User.order(:id).all, root: false end def show render json: user, root: false end

def create user = User.create!(safe_params) render json: user, root: false end def update user.update_attributes(safe_params) head :no_content end

def destroy user.destroy render nothing: true end

private def verify_is_admin (current_user.nil?) ? redirect_to(root_path) : (redirect_to(root_path) unless current_user.admin?) end

def user @user ||= User.find(params[:id]) end

def safe_params params.permit(:id, :title, :first_name, :last_name, :name, :email, :job_title, :organisation, :approved, :admin) end

end

Rails Routes

namespace :api, defaults: {format: :json} do devise_scope :user do resource :session, only: [:create, :destroy] resource :registrations end

Rails Routes

$ rake routes

api_users GET api/users#index {:format=>:json}POST api/users#create {:format=>:json}new_api_user GET api/users#new {:format=>:json}edit_api_user GET api/users#edit {:format=>:json}

....etc

Rails Serializer

Serializers/users_serializer.rb

class UserSerializer < ActiveModel::Serializer attributes :id,

:title, :first_name, :last_name, :email, :approved, :admin

end

Angular Routes

Javascripts/assaypipelineApp.js

$routeProvider.when('/design', { templateUrl: '/templates/design/design.html', controller: 'DesignViewCtrl' });

$routeProvider.when('/batches/:panel_id', { templateUrl: '/templates/panel/panel.html', controller: 'PanelController' });

$routeProvider.when('/datasets', { templateUrl: '/templates/datasets/datasets.html', controller: 'DatasetsController' });

Angular “View Controller”

angular.module('assaypipelineApp').controller("PanelsViewCtrl", ['$http', '$scope', '$routeParams', '$location', 'Batch', function($http, $scope, $routeParams, $location, Batch) { // Initialize by loading panels Batch.index().$promise.then(function(batches) { $scope.panels = batches; }, reportError("loading panels"));

Angular Model

$scope.panels = batches;

Fields defined in Rails controller / serializer

def indexdata = panels.as_json(methods: :user_name,

only [:id, :name, :description … render json: data, root: false

end

Rails -> Angular JSON

More complex Angular models

$scope.selector = { selectedGene: '', selectedExons: [], exons: [], regionChrom: '', regionStart: '', regionEnd: '', configVisible: false, paneldata: { batch_type: 'singleplex',

primer_runs: 20 tiling_sequence: '', min_primer_tm: 60,

panels.html

<span ng-if="panel.public_panel == true"> <span class="label label-primary">Public</span></span> <button class="btn btn-info btn-xs"

ng-click="duplicatePanel(panel)”type="button">Copy

</button>

<a href="/batches/{{panel.id}}">{{panel.name}} - {{panel.id}}</a><br/><span>{{panel.description}}</span><br/><small>Updated {{panel.updated_at}}</small>

Angular < = > Rails

$http Lowest level Construct urls manually Similar to jQuery / AJAX

$resource Angular wrapper around $http Gives the basic REST verbs for free Usually factor out into a service

Restangular Easiest by far Handles nesting Overkill for non-REST fall back to http for non-restful

$http

function getAssemblies() { $http({ method: 'GET', url: '/api/assemblies/' }).success(function(data) {

console.log(data);$scope.availableAssemblies = data;

}) };

Restangular

// Restangular returns promisesRestangular.all('users').getList() // GET: /users.then(function(users) { // returns a list of users $scope.user = users[0]; // first Restangular obj in list: { id: 123 }})

// Restangular objects are self-aware and know how to make their own RESTful requests$scope.user.getList('cars'); // GET: /users/123/cars

// You can also use your own custom methods on Restangular objects$scope.user.sendMessage(); // POST: /users/123/sendMessage

// Chain methods together to easily build complex requests$scope.user.one('messages', 123).one('from', 123).getList('unread');// GET: /user/123/messages/123/from/123/unread

Resources

Riding Rails with AngularJShttp://www.fullstack.io/edu/angular/rails/

AngularJS Railshttp://www.angularails.com/

egghead.io