Rails and AngularJS
-
Upload
yardley-pittman -
Category
Documents
-
view
77 -
download
5
description
Transcript of 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
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
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; } ]);
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
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