Globalcode	
  –	
  Open4education
Interfaces ricas com Rails e React.JS
Rodrigo Urubatan
@urubatan
Globalcode	
  –	
  Open4education
Quem?
Programador desde 1997
Trabalha com Ruby na Brightwire
Escreveu "Ruby On Rails: Desenvolvimento fácil e Rápido de
aplicações web”
Já trabalhou com diversas linguagens (C, C++, Delphi, PHP,
ASP, ColdFusion, VisualBasic, C#, Python, Ruby, Assembly,
…)
Apaixonado por Agile e atualmente por trabalho remoto
Patinador, Corredor, Ciclista e agora resolveu aprender
Karate :D
Pai de um Guri de 6 anos
http://urubatan.com.br - Anda meio abandonado mas vai voltar
http://sobrecodigo.com - idem
Globalcode	
  –	
  Open4education
Objetivo
Usar o Rails como backend da aplicação
Toda a interação com o usuário será implementada
no cliente
Validações e regras de negócio serão
implementadas no servidor
(sim, eu poderia usar Sinatra mas sou preguiçoso)
Globalcode	
  –	
  Open4education
Cuidado!
Nesta palestra vamos falar de uma SPA (Single
Page Application)
Isto tem vantagens e desvantagens
Melhora muito a interação com o usuário sem
duplicação de código, já que o código de
renderização fica todo no JS
Piora muito a indexação da sua aplicação por
buscadores (adeus SEO - ou não…)
Globalcode	
  –	
  Open4education
Criando a aplicação
Uma aplicação padrão Rails (rails new …)
Gemfile updates
gem 'backbone-on-rails'
gem 'react-rails', github: 'reactjs/react-rails', ref:
'master'
Environment update (development.rb para
começar)
config.react.variant = :development

config.react.addons = true # defaults to false

config.react.server_renderer =
React::ServerRendering::SprocketsRenderer
bundle install
rails g react:install
Globalcode	
  –	
  Open4education
Componentes
Componentes javascript vão ficar em app/assets/
javascript/components
Backbone.js vai facilitar a comunicação cliente/
servidor
arquivos .js.jsx tem uma facilidade extra, são
compilados pelo react-rails via asset pipeline e
permitem adicionar HTML inline
Globalcode	
  –	
  Open4education
Cadastrando um Usuário
rails g scaffold user name:string password:string
email:string
Apagar todas as views do usuário exceto
index.html.erb
Globalcode	
  –	
  Open4education
Alterações no controller
Fazer todos os métodos retornarem json
Remover edit, new e show
Apagar todo o código de index.html.erb e mudar
para:

<%= react_component('Layout', {}, {prerender:
false}) %>
Globalcode	
  –	
  Open4education
Agora mãos a obra
já temos uma “API" em Rails, poderíamos ter o
código em Sinatra que seria mais leve, mas eu
gosto do asset pipeline e assim fica mais fácil para
um iniciante
Falta criar os componentes backbone para acessar
o backend
Criar os componentes react para a UI
Globalcode	
  –	
  Open4education
backbone.js
app/assets/javascripts/collections/users.js
var Users = Backbone.Collection.extend({

model: User,

url: '/users'

});
app/assets/jaascripts/models/user.js

var User = Backbone.Model.extend({



});
Globalcode	
  –	
  Open4education
layout.js.jsx
var Layout = React.createClass({

getInitialState: function(){

var users = null;

if (this.props.collection) {

users = new Users(this.props.collection);

} else {

users = new Users();

users.fetch({success:function(data){

this.forceUpdate();

}.bind(this)});

}

return {

collection: users,

model: new User()

};

},

editModel:function(model){

this.setState({

model: model

})

},

newModel:function(){

this.setState({

model: new User()

})

},

render: function () {

return (<div>

<div id="list">

<UserList collection={this.state.collection} editModel={this.editModel}/>

</div>

<div id="form">

<UserForm collection={this.state.collection} model={this.state.model}/>

<a onClick={this.newModel}>New User</a>

</div>

</div>);

}

});
Globalcode	
  –	
  Open4education
user_list.js.jsx
var UserList = React.createClass({

componentDidMount: function () {

this.props.collection.on("change", function () {

this.forceUpdate()

}, this);

this.props.collection.on("reset", function () {

this.forceUpdate()

}, this);

},

componentWillUnmount: function () {

this.props.collection.off(null, null, this);

},

render: function () {

var users = this.props.collection.map(function (model) {

var editModel = function () {

this.props.editModel(model);

};

return (

<tr key={model.get("id")}>

<td><a href=“#” onClick={editModel.bind(this)}>{model.get("name")}</a></td>

<td>{model.get("email")}</td>

<td><a href=“#” onClick={model.destroy}>X</a></td>

</tr>

);

}.bind(this));

return (

<table className="user-list">

<thead>

<tr>

<th>Name</th>

<th>Email</th>

<th>&nbsp;</th>

</tr>

</thead>

<tbody>

{users}

</tbody>

</table>

);

}

});
Globalcode	
  –	
  Open4education
user_form.js.jsx
var UserForm = React.createClass({

submitUser: function () {

if(!this.props.model.get("id")){

this.props.collection.create(this.props.model)

}else{

this.props.model.save();

}

},

render: function () {

var label = this.props.model.get("id") ? "Update" : "Create";

return (

<div className="user-form">

<InputWithLabel model={this.props.model} label="Name" name="name" type="text"/>

<InputWithLabel model={this.props.model} label="Email" name="email" type="text"/>

<InputWithLabel model={this.props.model} label="Password" name="password" type="password"/>

<div className="form-field">

<button onClick={this.submitUser}>{label}</button>

</div>

</div>

);

}

});
Globalcode	
  –	
  Open4education
input_with_label.js.jsx
var InputWithLabel = React.createClass({

handleChange: function(event) {

this.props.model.set(this.props.name,event.target.value)

},

render: function() {

return <div className="form-field">

<label htmlFor={this.props.name}>{this.props.label}</label>

<div>

<input id={this.props.name} type={this.props.type} name={this.props.name} ref="input" onChange={this.handleChange}
value={this.props.model.get(this.props.name)}/>

</div>

</div>;

}

});
Globalcode	
  –	
  Open4education
O que, quando, onde e por
que?
Muitas aplicações hoje em dia exigem um nível alto
de interação com o usuário
Implementar isto usando bibliotecas mais baixo
nível é muito fácil de causar uma grande
confusão do código (PHP alguem?)
Componentização evita duplicação de código e
facilita a organização
Globalcode	
  –	
  Open4education
Globalcode	
  –	
  Open4education
indexação? performance?
renderização no servidor:

<%= react_component('Layout', {collection:
@users}, {prerender: true}) %>
components.js

//= require underscore

//= require backbone

//= require_tree ./models

//= require_tree ./collections

//= require_tree ./components

Globalcode	
  –	
  Open4education
Mas é só isto?
React-router
Backbone.Router
Flux - arquitetura JS usada pelo Facebook
Globalcode	
  –	
  Open4education

TDC São Paulo 2015 - Interfaces Ricas com Rails e React.JS

  • 1.
    Globalcode  –  Open4education Interfacesricas com Rails e React.JS Rodrigo Urubatan @urubatan
  • 2.
    Globalcode  –  Open4education Quem? Programadordesde 1997 Trabalha com Ruby na Brightwire Escreveu "Ruby On Rails: Desenvolvimento fácil e Rápido de aplicações web” Já trabalhou com diversas linguagens (C, C++, Delphi, PHP, ASP, ColdFusion, VisualBasic, C#, Python, Ruby, Assembly, …) Apaixonado por Agile e atualmente por trabalho remoto Patinador, Corredor, Ciclista e agora resolveu aprender Karate :D Pai de um Guri de 6 anos http://urubatan.com.br - Anda meio abandonado mas vai voltar http://sobrecodigo.com - idem
  • 3.
    Globalcode  –  Open4education Objetivo Usaro Rails como backend da aplicação Toda a interação com o usuário será implementada no cliente Validações e regras de negócio serão implementadas no servidor (sim, eu poderia usar Sinatra mas sou preguiçoso)
  • 4.
    Globalcode  –  Open4education Cuidado! Nestapalestra vamos falar de uma SPA (Single Page Application) Isto tem vantagens e desvantagens Melhora muito a interação com o usuário sem duplicação de código, já que o código de renderização fica todo no JS Piora muito a indexação da sua aplicação por buscadores (adeus SEO - ou não…)
  • 5.
    Globalcode  –  Open4education Criandoa aplicação Uma aplicação padrão Rails (rails new …) Gemfile updates gem 'backbone-on-rails' gem 'react-rails', github: 'reactjs/react-rails', ref: 'master' Environment update (development.rb para começar) config.react.variant = :development
 config.react.addons = true # defaults to false
 config.react.server_renderer = React::ServerRendering::SprocketsRenderer bundle install rails g react:install
  • 6.
    Globalcode  –  Open4education Componentes Componentesjavascript vão ficar em app/assets/ javascript/components Backbone.js vai facilitar a comunicação cliente/ servidor arquivos .js.jsx tem uma facilidade extra, são compilados pelo react-rails via asset pipeline e permitem adicionar HTML inline
  • 7.
    Globalcode  –  Open4education Cadastrandoum Usuário rails g scaffold user name:string password:string email:string Apagar todas as views do usuário exceto index.html.erb
  • 8.
    Globalcode  –  Open4education Alteraçõesno controller Fazer todos os métodos retornarem json Remover edit, new e show Apagar todo o código de index.html.erb e mudar para:
 <%= react_component('Layout', {}, {prerender: false}) %>
  • 9.
    Globalcode  –  Open4education Agoramãos a obra já temos uma “API" em Rails, poderíamos ter o código em Sinatra que seria mais leve, mas eu gosto do asset pipeline e assim fica mais fácil para um iniciante Falta criar os componentes backbone para acessar o backend Criar os componentes react para a UI
  • 10.
    Globalcode  –  Open4education backbone.js app/assets/javascripts/collections/users.js varUsers = Backbone.Collection.extend({
 model: User,
 url: '/users'
 }); app/assets/jaascripts/models/user.js
 var User = Backbone.Model.extend({
 
 });
  • 11.
    Globalcode  –  Open4education layout.js.jsx varLayout = React.createClass({
 getInitialState: function(){
 var users = null;
 if (this.props.collection) {
 users = new Users(this.props.collection);
 } else {
 users = new Users();
 users.fetch({success:function(data){
 this.forceUpdate();
 }.bind(this)});
 }
 return {
 collection: users,
 model: new User()
 };
 },
 editModel:function(model){
 this.setState({
 model: model
 })
 },
 newModel:function(){
 this.setState({
 model: new User()
 })
 },
 render: function () {
 return (<div>
 <div id="list">
 <UserList collection={this.state.collection} editModel={this.editModel}/>
 </div>
 <div id="form">
 <UserForm collection={this.state.collection} model={this.state.model}/>
 <a onClick={this.newModel}>New User</a>
 </div>
 </div>);
 }
 });
  • 12.
    Globalcode  –  Open4education user_list.js.jsx varUserList = React.createClass({
 componentDidMount: function () {
 this.props.collection.on("change", function () {
 this.forceUpdate()
 }, this);
 this.props.collection.on("reset", function () {
 this.forceUpdate()
 }, this);
 },
 componentWillUnmount: function () {
 this.props.collection.off(null, null, this);
 },
 render: function () {
 var users = this.props.collection.map(function (model) {
 var editModel = function () {
 this.props.editModel(model);
 };
 return (
 <tr key={model.get("id")}>
 <td><a href=“#” onClick={editModel.bind(this)}>{model.get("name")}</a></td>
 <td>{model.get("email")}</td>
 <td><a href=“#” onClick={model.destroy}>X</a></td>
 </tr>
 );
 }.bind(this));
 return (
 <table className="user-list">
 <thead>
 <tr>
 <th>Name</th>
 <th>Email</th>
 <th>&nbsp;</th>
 </tr>
 </thead>
 <tbody>
 {users}
 </tbody>
 </table>
 );
 }
 });
  • 13.
    Globalcode  –  Open4education user_form.js.jsx varUserForm = React.createClass({
 submitUser: function () {
 if(!this.props.model.get("id")){
 this.props.collection.create(this.props.model)
 }else{
 this.props.model.save();
 }
 },
 render: function () {
 var label = this.props.model.get("id") ? "Update" : "Create";
 return (
 <div className="user-form">
 <InputWithLabel model={this.props.model} label="Name" name="name" type="text"/>
 <InputWithLabel model={this.props.model} label="Email" name="email" type="text"/>
 <InputWithLabel model={this.props.model} label="Password" name="password" type="password"/>
 <div className="form-field">
 <button onClick={this.submitUser}>{label}</button>
 </div>
 </div>
 );
 }
 });
  • 14.
    Globalcode  –  Open4education input_with_label.js.jsx varInputWithLabel = React.createClass({
 handleChange: function(event) {
 this.props.model.set(this.props.name,event.target.value)
 },
 render: function() {
 return <div className="form-field">
 <label htmlFor={this.props.name}>{this.props.label}</label>
 <div>
 <input id={this.props.name} type={this.props.type} name={this.props.name} ref="input" onChange={this.handleChange} value={this.props.model.get(this.props.name)}/>
 </div>
 </div>;
 }
 });
  • 15.
    Globalcode  –  Open4education Oque, quando, onde e por que? Muitas aplicações hoje em dia exigem um nível alto de interação com o usuário Implementar isto usando bibliotecas mais baixo nível é muito fácil de causar uma grande confusão do código (PHP alguem?) Componentização evita duplicação de código e facilita a organização
  • 16.
  • 17.
    Globalcode  –  Open4education indexação?performance? renderização no servidor:
 <%= react_component('Layout', {collection: @users}, {prerender: true}) %> components.js
 //= require underscore
 //= require backbone
 //= require_tree ./models
 //= require_tree ./collections
 //= require_tree ./components

  • 18.
    Globalcode  –  Open4education Masé só isto? React-router Backbone.Router Flux - arquitetura JS usada pelo Facebook
  • 19.