Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Interfaces ricas com Rails e React.JS @ Rubyconf 2015

713 views

Published on

Slides da palestra que apresentei na Rubyconf 2015 sobre Rails e React.JS

Published in: Software
  • Be the first to comment

Interfaces ricas com Rails e React.JS @ Rubyconf 2015

  1. 1. Globalcode  – Open4education Interfaces  ricas  com  Rails  e  React.JS Rodrigo  Urubatan @urubatan
  2. 2. 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
  3. 3. Globalcode  – Open4education Objetivo (O blog mais feio do mundo!)
  4. 4. 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)
  5. 5. 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…)
  6. 6. 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
  7. 7. 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
  8. 8. Globalcode  – Open4education Cadastrando um Usuário rails g scaffold post title:string slug:text content:text Apagar todas as views do post exceto index.html.erb Apagar todo o código de index.html.erb e mudar para: <%= react_component('Blog', {collection: @posts, item: @post}) %>
  9. 9. Globalcode  – Open4education Alterações no controller Fazer todos os métodos retornarem json, mais ou menos assim (não é a melhor tecnica, mas boa o suficiente para o exemplo) class PostsController < ApplicationController before_action :set_post, only: [:show, :edit, :update, :destroy] def index @posts = Post.all end def show @posts = Post.all render :index end # GET /posts/new def new @post = Post.new render :index end # POST /posts # POST /posts.json def create @post = Post.new(post_params) respond_to do |format| if @post.save format.json { render json: @post, status: :created, location: @post } else format.json { render json: @post.errors, status: :unprocessable_entity } end end end #  PATCH/PUT  /posts/1 #  PATCH/PUT  /posts/1.json def update respond_to do  |format| if  @post.update(post_params) format.json {  render  json:  @user,  status:  :ok,  location:  @post  } else format.json {  render  json:  @post.errors,  status:  :unprocessable_entity } end end end #  DELETE  /posts/1 #  DELETE  /posts/1.json def destroy @post.destroy respond_to do  |format| format.json {  head  :no_content } end end private #  Use  callbacks  to  share  common  setup  or  constraints  between  actions. def set_post @post  =  Post.find_by(slug:  params[:id])  ||  Post.find(params[:id]) end #  Never  trust  parameters  from  the  scary  internet,  only  allow  the  white  list  through. def post_params params.require(:post).permit(:title,  :slug,  :content) end end
  10. 10. 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
  11. 11. Globalcode  – Open4education backbone.js app/assets/javascripts/collections/posts.js var Posts = Backbone.Collection.extend({ model: Post, url: '/posts' }); app/assets/jaascripts/models/post.js var Post = Backbone.Model.extend({ });
  12. 12. Globalcode  – Open4education blog.js.jsx var Blog = React.createClass({ getInitialState: function(){ var posts = null; var post = null; if (this.props.collection) { posts = new Posts(this.props.collection); } else { posts = new Posts(); posts.fetch({success:function(data){ this.forceUpdate(); }.bind(this)}); } if (this.props.item) { post = new Post(this.props.item); } else { post = new Post(); } return { collection: posts, model: post, editing: false }; }, componentDidMount:function(){ this.router = new PostRouter({blog: this}); Backbone.history.start({pushState: true, root: "/"}); }, editModel:function(model){ this.setState({ model: model, editing: true }) }, viewModel:function(model){ this.setState({ model:  model, editing:  false });; this.router.navigate("/posts/"  +  model.get("slug"),  {trigger:  true});; }, newModel:function(){ this.setState({ model:  new  Post(), editing:  true }) }, render:  function   ()  { if(this.state.editing)  { return  (<div> <div  id="blogList"> <PostList collection={this.state.collection}  viewModel={this.viewModel}   newModel={this.newModel}/> </div> <div  id="blogPost"> <PostForm collection={this.state.collection}  model={this.state.model}   viewModel={this.viewModel}/> </div> </div>);; }else{ return  (<div> <div  id="blogList"> <PostList collection={this.state.collection}  viewModel={this.viewModel}   newModel={this.newModel}/> </div> <div  id="blogPost"> <PostView model={this.state.model}  editModel={this.editModel}/> </div> </div>);; } } });;
  13. 13. Globalcode  – Open4education post_list.js.jsx var PostList = 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 viewModel = function () { this.props.viewModel(model); }; return ( <tr key={model.get("id")}> <td><a href="#" onClick={viewModel.bind(this)}>{model.get("title")}</a></td> <td>{new Date(model.get("created_at")).toDateString()}</td> </tr> ); }.bind(this)); return ( <div> <table className="post-list"> <thead> <tr> <th>Post</th> <th>Published</th> </tr> </thead> <tbody> {users} </tbody> </table> <hr/> <a href="#" onClick={this.props.newModel}>Create post</a> </div> ); } });
  14. 14. Globalcode  – Open4education post_view.js.jsx var PostView = React.createClass({ editModel: function () { this.props.editModel(this.props.model); }, render: function () { var innerLines = null; if(this.props.model.get("content")) { innerLines=_.map(this.props.model.get("content").split("n"), function (txt, idx) { return <p key={idx}>{txt}</p> }); } return ( <div className="blog-post"> <h1><a href={this.props.model.get("slug")}>{this.props.model.get("title")}</a></h1> <div className="post-body"> {innerLines} </div> <hr/> <a href="#" onClick={this.editModel}>Edit post</a> </div> ); } });
  15. 15. Globalcode  – Open4education post_form.js.jsx var PostForm = React.createClass({ saveModel: function () { if (this.props.model.get("id")) { this.props.model.save(); } else { this.props.collection.create(this.props.model); } this.props.viewModel(this.props.model) }, render: function () { return ( <div className="blog-post"> <InputWithLabel model={this.props.model} label="Title" name="title" type="text"/> <InputWithLabel model={this.props.model} label="Body" name="content" type="textarea"/> <div className="form-field"> <button onClick={this.saveModel}>Save</button> </div> </div> ); } });
  16. 16. 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>; } });
  17. 17. 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
  18. 18. Globalcode  – Open4education
  19. 19. Globalcode  – Open4education indexação? performance? renderização no servidor: <%= react_component('Blog', {collection: @posts, item: @post}, {prerender: true}) %> components.js //= require underscore //= require backbone //= require_tree ./models //= require_tree ./collections //= require_tree ./components
  20. 20. Globalcode  – Open4education Mas é só isto? React-router Backbone.Router Flux - arquitetura JS usada pelo Facebook Pré processamento com Gulp (sintaxe do ECMAScript 6 suportada)
  21. 21. Globalcode  – Open4education

×