1. The document discusses integrating front-end frameworks like Angular, Ember, and React with Rails by constructing JSON APIs, preloading data to avoid loading screens, and server-side rendering for SEO and performance.
2. It provides code examples for building a TODO application with each framework, including using ngResource and factories in Angular, Ember conventions like Ember Data, and building isolated React components.
3. Server-side rendering is highlighted as the future for isomorphic JavaScript, providing benefits like prerendering on initial page load.
Post Quantum Cryptography – The Impact on Identity
Crossing the Bridge: Connecting Rails and your Front-end Framework
1. Crossing the Bridge:Crossing the Bridge:
Connecting Rails and your Front-endConnecting Rails and your Front-end
FrameworkFramework
@danielspecs@danielspecs
2. This is what we're trying to avoidThis is what we're trying to avoid
9. Always thinkAlways think
about theabout the
bigger picturebigger picture
You will encounter a lot ofYou will encounter a lot of
tradeoffs.tradeoffs.
10. Some of the fun thatSome of the fun that
awaits...awaits...
Duplicated modelsDuplicated models
Separate codebasesSeparate codebases
ComplexityComplexity
13. What do people want?What do people want?
Maintainable,Maintainable,
sustainable,sustainable,
performantperformant
applicationsapplications
14. But now that you'veBut now that you've
been warned...been warned...
15. Holy s**t canHoly s**t can
you make someyou make some
awesomeawesome
applications.applications.
16. RecapRecap
Never lose sight of the ultimate goalNever lose sight of the ultimate goal
Understand the tradeoffs that willUnderstand the tradeoffs that will
comecome
There may be a solutionThere may be a solution
18. What we're going to be building:What we're going to be building:
TodoMVC on RailsTodoMVC on Rails
Scaffolding out the same application in each ofScaffolding out the same application in each of
the frameworks makes it easy to referencethe frameworks makes it easy to reference
20. Now let's have a lookNow let's have a look
at Angularat Angular
21. Developed by GoogleDeveloped by Google
Two-way data bindingTwo-way data binding
Dependency InjectionDependency Injection
But... Angular 2But... Angular 2
22. First, let's get ourFirst, let's get our
Rails project API readyRails project API ready
23. # app/controllers/api/todos_controller.rb
class Api::TodosController < ApplicationController
respond_to :json
def index
@todos = Todo.all
render json: @todos
end
def create
@todo = Todo.create(todo_params)
render json: @todo
end
private
def todo_params
params.require(:todo).permit(:item)
end
end
# config/routes.rb
namespace :api, :defaults => {:format => :json} do
resources :todos, only: [:index, :create]
end
# app/models/todo.rb
class Todo < ActiveRecord::Base
end
24. There's no official AngularThere's no official Angular
integration with Rails...integration with Rails...
So that's a perfect opportunity toSo that's a perfect opportunity to
try out Bower.try out Bower.
25. Created by Twitter
One centralized location for packages
Can be integrated with Rails via the bower-rails gem
27. How can we manageHow can we manage
our client-side dataour client-side data
to make it easy toto make it easy to
work with?work with?
28. ngResource is an optional libraryngResource is an optional library
to map basic CRUD actions toto map basic CRUD actions to
specific method calls.specific method calls.
Let's scaffold out a basicLet's scaffold out a basic
Angular and see how we canAngular and see how we can
integrate ngResourceintegrate ngResource
29. // app/assets/main.js
// This is the main entry point for our application.
var Todo = angular
.module('todo', ['ngResource', 'ngRoute'])
.config(['$routeProvider', '$httpProvider', function($routeProvider, $httpProvider) {
// We need to add this for Rails CSRF token protection
$httpProvider.defaults.headers.common['X-CSRF-Token'] =
$('meta[name=csrf-token]').attr('content');
// Right now we have one route but we could have as many as we want
$routeProvider
.when("/",
{templateUrl: "../assets/index.html",
controller: "TodoCtrl"})
}]);
# config/application.rb
config.assets.paths << "#{Rails.root}/app/assets/templates"
30. Now we can set up our factory toNow we can set up our factory to
hold our resource and pass it tohold our resource and pass it to
our controller and our templateour controller and our template
33. RecapRecap
1. Data binding in Angular is powerfulData binding in Angular is powerful
2. ngResource makes requests easyngResource makes requests easy
3. Multiple API calls to initialize application canMultiple API calls to initialize application can
get trickyget tricky
35. Created by Tom Dale andCreated by Tom Dale and
Yehuda KatzYehuda Katz
Made for large, ambitiousMade for large, ambitious
applicationsapplications
Favors convention overFavors convention over
configurationconfiguration
Ember Data is absolutelyEmber Data is absolutely
wonderfulwonderful
37. What we'll beWhat we'll be
working withworking with
class User < ActiveRecord::Base
has_many :todos
end
class Todo < ActiveRecord::Base
belongs_to :user
end
class EmberController < ApplicationController
def preload
@todos = current_user.todos
end
end
Rails.application.routes.draw do
root 'ember#preload'
end
39. $ rails g serializer todo
create app/serializers/todo_serializer.rb
class TodoSerializer < ActiveModel::Serializer
embed :ids, include: true
attributes :id, :name
end
{
"todos": [
{
"id": 1,
"name": "Milk"
},
{
"id": 2,
"name": "Coffee"
},
{
"id": 3,
"name": "Cupcakes"
}
]
}
Create a new serializer, set it up to workCreate a new serializer, set it up to work
with Emberwith Ember
40. Now that we're all set up, whatNow that we're all set up, what
are we trying to accomplish?are we trying to accomplish?
Instead of using JSON calls, weInstead of using JSON calls, we
want to preload Emberwant to preload Ember
41. Why?Why?
Minimize round trips to the serverMinimize round trips to the server
Bootstrapping the app means a quickerBootstrapping the app means a quicker
experience for our usersexperience for our users
42. # app/controllers/ember_controller.rb
class EmberController < ApplicationController
def preload
@todos = current_user.todos
preload! @todos, serializer: TodoSerializer
end
def preload!(data, opts = {})
@preload ||= []
data = prepare_data(data, opts)
@preload << data unless data.nil?
end
def prepare_data(data, opts = {})
data = data.to_a if data.respond_to? :to_ary
data = [data] unless data.is_a? Array
return if data.empty?
options[:root] ||= data.first.class.to_s.underscore.pluralize
options[:each_serializer] = options[:serializer] if options[:serializer]
ActiveModel::ArraySerializer.new(data, options)
end
end
43. We'll pass this to Ember via theWe'll pass this to Ember via the
window.window.
# app/views/layouts/application.html.haml
= stylesheet_link_tag :frontend
:javascript
window.preloadEmberData = #{(@preload || []).to_json};
= include_ember_script_tags :frontend
%body
= yield
github.com/hummingbird-me/hummingbird
44. Let's get setup withLet's get setup with
our client-side codeour client-side code
45. $ rails g ember-cli:init
create config/initializers/ember.rb
# config/initializer/ember.rb
EmberCLI.configure do |config|
config.app :frontend, path: Rails.root.join('frontend').to_s
end
$ ember new frontend --skip-git
version: 0.2.3
installing
create .bowerrc
create .editorconfig
create .ember-cli
create .jshintrc
create .travis.yml
create Brocfile.js
create README.md
create app/app.js
create app/components/.gitkeep
...
46. $ ember g resource todos
version: 0.2.3
installing
create app/models/todo.js
installing
create tests/unit/models/todo-test.js
installing
create app/routes/todos.js
create app/templates/todos.hbs
installing
create tests/unit/routes/todos-test.js
$ ember g adapter application
version: 0.2.3
installing
create app/adapters/application.js
installing
create tests/unit/adapters/application-test.js
$ ember g serializer application
version: 0.2.3
installing
create app/serializers/application.js
installing
create tests/unit/serializers/application-test.js
47. // frontend/app/models/todo.js
import DS from 'ember-data';
var Todo = DS.Model.extend({
name: DS.attr('string')
});
export default Todo;
// frontend/app/adapters/application.js
import DS from 'ember-data';
export default DS.ActiveModelAdapter.extend({
});
48. // frontend/app/initializers/preload.js
export function initialize(container) {
if (window.preloadEmberData) {
var store = container.lookup('store:main');
window.preloadEmberData.forEach(function(item) {
store.pushPayload(item);
});
}
}
export default {
name: 'preload',
after: 'store',
initialize: initialize
};
49. Ember will initializeEmber will initialize
Ember Data objectsEmber Data objects
for us, inferring thefor us, inferring the
correct type from thecorrect type from the
root of the JSONroot of the JSON
responseresponse
50. Now we can use our route to find the data
and render it via a template
// frontend/app/router.js
export default Router.map(function() {
this.resource('todos', { path: '/' }, function() {});
});
// frontend/app/routes/todos/index.js
export default Ember.Route.extend({
model: function() {
return this.store.all('todo')
}
});
// frontend/app/templates/todos/index.hbs
<h2>Todo:</h2>
<ul>
{{#each todo in model}}
<li>{{todo.name}}</li>
{{/each}}
</ul>
51. RecapRecap
1. Don't fight Ember. Use conventionsDon't fight Ember. Use conventions
like AMSlike AMS
2. Preloading is extremely powerfulPreloading is extremely powerful
3. Avoiding spinners and loading screens
means a great experience
54. No initial API call, noNo initial API call, no
preloading, renderpreloading, render
straight from thestraight from the
server.server.
http://bensmithett.com/server-rendered-react-components-in-rails/
55. # app/controllers/todos_controller.rb
class TodosController < ApplicationController
def index
@load = {
:todos => current_user.todos,
:form => {
:action => todos_path,
:csrf_param => request_forgery_protection_token,
:csrf_token => form_authenticity_token
}
}
end
def create
@todo = Todo.create(todo_params)
render json: @todo
end
def todo_params
params.require(:todo).permit(:name)
end
end
56. Use the react-rails gemUse the react-rails gem
# Gemfile
gem "react-rails"
$ rails g react:install
58. React is builtReact is built
around componentsaround components
Each component should have one isolated
responsibility.
59. # app/assets/javascripts/components/_todos.js.jsx
var Todos = React.createClass({
getInitialState: function () {
return JSON.parse(this.props.load);
},
newTodo: function ( formData, action ) {
$.ajax({
data: formData,
url: action,
type: "POST",
dataType: "json",
success: function (data) {
this.setState({todos: this.state.todos.concat([data]});
}.bind(this)
});
},
render: function () {
return (
<div>
<ul>
<TodosList todos={this.state.todos} />
</ul>
<TodoForm form={this.state.form} onNewTodo={this.newTodo} />
</div>
);
}
});
60. TodosList ComponentTodosList Component
// app/assets/javascripts/components/_todos_list.js.jsx
var TodosList = React.createClass({
render: function () {
var allTodos = this.props.todos.map(function (todo) {
return <Todo name={todo.name} />
});
return (
<div>
{ allTodos }
</div>
)
}
});
// app/assets/javascripts/components/_todo.js.jsx
var Todo = React.createClass({
render: function (){
return (
<div>
<li>{this.props.name}</li>
</div>
)
}
});
61. And now the form...And now the form...
// app/assets/javascripts/components/_todo_form.js.jsx
var TodoForm = React.createClass({
handleSubmit: function (e) {
e.preventDefault();
var formData = $(this.refs.form.getDOMNode()).serialize();
this.props.onNewTodo(formData, this.props.form.action);
this.refs.name.getDOMNode().value = "";
},
render: function () {
return (
<form ref="form"action={this.props.form.action} method="post" onSubmit={this.handleSubmit}>
<input type="hidden" name={this.props.form.csrf_param } value={this.props.form.csrf_token}/>
<input ref="name" name="todo[name]" placeholder="I need to do..." />
<button type="submit">New Todo</button>
</form>
)
}
});
62. RecapRecap
1. Each component should have only oneEach component should have only one
responsibilityresponsibility
2. Prerender on the server for SEO, usability andPrerender on the server for SEO, usability and
other benefitsother benefits
3. UJS will mount your component and take careUJS will mount your component and take care
of the handoffof the handoff
64. Where we've come from andWhere we've come from and
where we are goingwhere we are going
1. Constructing API's that serve JSON to the clientConstructing API's that serve JSON to the client
2. Preload your data on startup to avoid spinners andPreload your data on startup to avoid spinners and
loading screensloading screens
3. Server-side rendering for SEO, startup time and a greatServer-side rendering for SEO, startup time and a great
user experienceuser experience
65. Thanks!Thanks!
Would love to answer any questions
Please feel free to tweet at me or get in
touch in any other way.
@danielspecs
spector.io