Aplicacoes dinamicas Rails com Backbone

Post on 08-May-2015

1,471 views 0 download

description

Apresentação na RubyConfBR sob

transcript

Aplicações dinâmicas em

Rails

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(); } }); }});

http://backbone-todos.heroku.com/