Post on 08-May-2015
description
transcript
Aplicações dinâmicas em
Rails
Rafael Felix
http://www.crafters.com.br
http://blog.rollingwithcode.com
@rs_felix
Backbone é uma estrutura para aplicações que fazem uso pesado de JavaScript, e
conecta-se a sua aplicação por uma interface RESTful.
Backbone.Model
var MyModel = Backbone.Model.extend({})
var MyModel = Backbone.Model.extend({})
save([attributes],[options])
var MyModel = Backbone.Model.extend({})
save([attributes],[options])
POST PUT
var MyModel = Backbone.Model.extend({})
save([attributes],[options])
fetch([options])
var MyModel = Backbone.Model.extend({})
save([attributes],[options])
fetch([options]) setInterval(function(){ model.fetch();
}, 10000);
var MyModel = Backbone.Model.extend({})
save([attributes],[options])
fetch([options]) validate(attributes)
var Chapter = Backbone.Model.extend({ validate: function(attrs) { if (attrs.end < attrs.start) { return "can't end before it starts"; } }});
var one = new Chapter({title : "Chapter One: The Beginning"
});
one.bind("error", function(model, error) {alert(model.get("title") + " " + error);
});
one.set({start: 15,
end: 10 });
var Chapter = Backbone.Model.extend({ validate: function(attrs) { if (attrs.end < attrs.start) { return "can't end before it starts"; } }});
var one = new Chapter({title : "Chapter One: The Beginning"
});
one.bind("error", function(model, error) {alert(model.get("title") + " " + error);
});
one.set({start: 15,
end: 10 });
Backbone.Model
Backbone.Model
Backbone.Collection
var Library = Backbone.Collection.extend({ model: Book});
var Library = Backbone.Collection.extend({ model: Book});
add(models, [options])
var Library = Backbone.Collection.extend({ model: Book});
add(models, [options]) url()
var Library = Backbone.Collection.extend({ model: Book});
add(models, [options]) url()url: '/library'
var Library = Backbone.Collection.extend({ model: Book});
add(models, [options]) url()
fetch([options])
var Library = Backbone.Collection.extend({ model: Book});
add(models, [options]) url()
fetch([options]) Library.fetch()
GET '/library'
var Library = Backbone.Collection.extend({ model: Book});
add(models, [options]) url()
fetch([options]) create(attributes, [options])
var alibrary = new Library;var book = alibrary.create({
title: "A book",author: "Someone"
})
Backbone.Model
Backbone.Collection
Backbone.Model
Backbone.Collection
Backbone.Router
var Workspace = Backbone.Router.extend({
routes: { "help": "help", "search/:query": "search", "search/:query/p:page": "search" },
help: function() {},
search: function(query, page) {}
});
var Workspace = Backbone.Router.extend({
routes: { "help": "help", "search/:query": "search", "search/:query/p:page": "search" },
help: function() {},
search: function(query, page) {}
});
#help#search/felix#search/felix/p2
var Workspace = Backbone.Router.extend({
routes: { "help": "help", "search/:query": "search", "search/:query/p:page": "search" },
help: function() {},
search: function(query, page) {}
});
#help#search/felix#search/felix/p2
var Workspace = Backbone.Router.extend({
routes: { "help": "help", "search/:query": "search", "search/:query/p:page": "search" },
help: function() {},
search: function(query, page) {}
});
#help#search/felix#search/felix/p2
var Workspace = Backbone.Router.extend({
routes: { "help": "help", "search/:query": "search", "search/:query/p:page": "search" },
help: function() {},
search: function(query, page) {}
});
#help#search/felix#search/felix/p2
felix 2
Backbone.Model
Backbone.Collection
Backbone.Router
Backbone.Model
Backbone.Collection
Backbone.RouterBackbone.View
var DocumentRow = Backbone.View.extend({
tagName: "li",
className: "document-row",
events: { "click .icon": "open", "click .button.edit": "openEditDialog", "click .button.delete": "destroy" },
render: function() { ... }
});
var DocumentRow = Backbone.View.extend({
tagName: "li",
className: "document-row",
events: { "click .icon": "open", "click .button.edit": "openEditDialog", "click .button.delete": "destroy" },
render: function() { ... }
});
<li class="document-row"></li>
var DocumentRow = Backbone.View.extend({
tagName: "li",
className: "document-row",
events: { "click .icon": "open", "click .button.edit": "openEditDialog", "click .button.delete": "destroy" },
render: function() { ... }
});
$(".icon").click(open)
Exemplo
layout
layoutapplication
layoutapplication
ProductView
layoutapplication
ProductView
CartView
click
click
Passo 1
layoutapplication
... <div class="container"> <div class="content" id="application"> </div> <footer> <p></p> </footer>
</div>...
app/views/layouts/application.html.erb
JavaScript Templates
var obj = "bla bla bla";someDiv = document.getElementById("someDiv");someDiv.innerHTML = "<span>" + obj + "</span>";
<span> ${obj} </span>
template.jst
<span> ${obj} </span>
template.jst
<span> <%= obj %> </span>
template.jst.ejs
app/assets/javascripts/templates/app.jst.ejs
<div id="products" class="span10"></div><div id="cart" class="span6"></div>
app/assets/javascripts/views/app_view.js
window.AppView = Backbone.View.extend({ template: JST["templates/app"], className: "row", initialize: function(){ }, render: function(){ $(this.el).html(this.template()); return this; }});
app/assets/javascripts/home.js
$(function(){ view = new AppView().render().el; $(view).appendTo("#application");});
app/assets/javascripts/home.js
$(function(){ view = new AppView().render().el; $(view).appendTo("#application");});
<div class="row"> <div id="products" class="span12"> </div> <div id="cart" class="span6"> </div></div>
Passo 2
ProductView
app/assets/javascripts/templates/product.jst.ejs
<div class="product-image"> <img class="thumbnail" src="http://placehold.it/90x90" alt=""></div><div class="details"> <span class="name"><%= model.get("name") %></span><br /> <span class="price">R$ <%= model.get("price") %></span> <form class="add_product"> <input type="hidden" name="id" value="<%= model.get("id") %>"> <input type="submit" name="commit" value="Comprar" class="btn info"> </form></div>
window.ProductView = Backbone.View.extend({ template: JST["templates/product"], className: "product-detail", initialize: function(){ }, render: function(){ $(this.el).html(this.template({model: this.model})); return this; }});
app/assets/javascripts/views/product_view.js
rails g model product name:string price:decimal
ActiveRecord::Base.include_root_in_json = false
config/initializers/backbone.rb
rails g controller products
rails g model product name:string price:decimal
ActiveRecord::Base.include_root_in_json = false
config/initializers/backbone.rb
rails g controller products
[ {"product": { "name" : "" }}, {"product": { "name": "" }},]
rails g model product name:string price:decimal
ActiveRecord::Base.include_root_in_json = false
config/initializers/backbone.rb
rails g controller products
[ {"name" : "" }, {"name": "" },]
app/controllers/products_controller.rb
class ProductsController < ApplicationController respond_to :json def index @products = Product.all respond_with @products endend
app/assets/javascripts/models/product.js
window.Product = Backbone.Model.extend({ });
window.ProductsCollection = Backbone.Collections.extend({ model: Product, url: '/products'});
app/assets/javascripts/views/app_view.js
window.AppView = Backbone.View.extend({
initialize: function(){ _.bindAll(this, 'addOne', 'addAll'); this.collection = new ProductsCollection; this.collection.bind('add', this.addOne); this.collection.bind('all', this.addAll); this.collection.fetch(); }, addAll: function(){ $("#products").html(""); this.collection.each(this.addOne); }, addOne: function(product){ view = new ProductView({model: product}).render().el; $(view).appendTo("#products"); }});
Passo 3
CartView
<div id="cart-products"></div><hr/>Total: <%= model.get("quantity") %> R$ <%= model.get("total") %>
app/assets/javascripts/templates/cart.jst.ejs
window.CartView = Backbone.View.extend({ template: JST["templates/cart"], className: "cart-detail", initialize: function(){ _.bindAll(this, 'render'); this.model.bind('change', this.render); this.model.fetch(); }, render: function(){ $(this.el).html(this.template({model: this.model})); return this; }});
app/assets/javascripts/views/cart_view.js
app/assets/javascripts/models/cart.js
window.Cart = Backbone.Model.extend({ url: function(){ return '/cart'; }});
rails g model cart quantity:integer total:decimal
rails g controller cart
app/controllers/cart_controller.rb
class CartController < ApplicationController respond_to :json def show @cart ||= Cart.first || Cart.create! respond_with @cart endend
app/controllers/cart_controller.rb
class CartController < ApplicationController respond_to :json def show @cart ||= Cart.first || Cart.create! respond_with @cart endend
get 'cart' => "cart#show"
app/assets/javascripts/views/app_view.js
window.AppView = Backbone.View.extend({...
initialize: function(){ ... this.cart = new Cart; this.cartView = new CartView({model: this.cart}). render().el; }, render: function(){ $(this.el).html(this.template()); this.$("#cart").html(this.cartView); return this; }, ...});
Passo 4
click
click
rails g model cart_product cart:references product:references
rails g model cart_product cart:references product:references
app/models/cart.rb
class Cart < ActiveRecord::Base has_many :cart_products has_many :products, through: :cart_products def add_product(product) self.update_attributes quantity: self.quantity + 1, total: total + product.price self.cart_products << CartProduct.new(cart: self, product: product) endend
app/assets/javascripts/models/cart_product.js
window.CartProduct = Backbone.Model.extend({ url: function(){ return "/cart/product/"+this.productId+"/add"; }, initialize: function(args){ this.productId = args.productId; }});
app/assets/javascripts/models/cart_product.js
post 'cart/product/:id/add' => "cart#add_product"
window.CartProduct = Backbone.Model.extend({ url: function(){ return "/cart/product/"+this.productId+"/add"; }, initialize: function(args){ this.productId = args.productId; }});
app/controllers/cart_controller.rb
class CartController < ApplicationController respond_to :json def show ... end def add_product @product = Product.find params[:id] @cart = Cart.first @cart.add_product @product respond_with @cart endend
app/assets/javascripts/views/product_view.js
window.ProductView = Backbone.View.extend({ ... events: { "submit form" : "addProductToCart" }, initialize: function(args){ ... this.cart = args.cart }, ... });
app/assets/javascripts/views/product_view.js
window.ProductView = Backbone.View.extend({ ... events: { "submit form" : "addProductToCart" }, initialize: function(args){ ... this.cart = args.cart }, ... });
app/assets/javascripts/views/app_view.js
addOne: function(product){ view = new ProductView({model: product, cart: this.cart}).render().el; $(view).appendTo("#products"); }
app/assets/javascripts/views/product_view.js
window.ProductView = Backbone.View.extend({ ... addProductToCart: function(e){ e.preventDefault(); productId = this.$("form.add_product > input[name=id]").val(); item = new CartProduct({productId: productId}); view = this; item.save({}, { success: function(){ view.cart.fetch(); } }); }});
Obrigadofelix.rafael@gmail.com
http://twitter.com/rs_felix
Linkshttp://documentcloud.github.com/backbone/
https://github.com/creationix/haml-js
https://github.com/codebrew/backbone-rails
http://seesparkbox.com/foundry/better_rails_apis_with_rabl
http://github.com/fellix