Your SlideShare is downloading. ×
Unobtrusive Ajax With Rails
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Unobtrusive Ajax With Rails

720
views

Published on


0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
720
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
8
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Unobtrusive Ajax With Rails Dan Webb (dan@danwebb.net)
  • 2. Overview ★ A bit of history ★ What is unobtrusive scripting? ★ The UJS Plugin ★ Casestudy ★ Ranting Upcoming UJS Features
  • 3. The dark days of DHTML
  • 4. Then web standards arrived
  • 5. The Client-side Cake Style - CSS Content - (X)HTML
  • 6. What web standards did for us ★ More maintainable ★ More accessible ★ Leaner pages ★ Platform independent (print, mobile...) ★ 'Future-proof' as well as backwards compatible
  • 7. JavaScript got a bad name
  • 8. It deserved it
  • 9. Web 2.0
  • 10. Folksonomies
  • 11. Massive text boxes
  • 12. Social software
  • 13. and Ajax...
  • 14. JavaScript is trendy again!
  • 15. Browser support is much better
  • 16. We learnt our lessons from the DHTML days
  • 17. What did we learn?
  • 18. Unobtrusive DOM Scripting
  • 19. It's an approach to browser UI design
  • 20. It's about separating content and style from behaviour
  • 21. Behaviour: A new layer for the client-side cake Behaviour - JavaScript Style - CSS Content - (X)HTML
  • 22. It's enhancing a working application
  • 23. so it degrades gracefully when things don't work
  • 24. It's not just about putting JavaScript in a different file
  • 25. It's not rocket science
  • 26. It's not the 'Rails Way'
  • 27. An example: link_to_remote
  • 28. <%= link_to_remote 'View description', :controller => 'product', :action => 'desc', :id => @product.id %>
  • 29. <a href="#" onclick="new Ajax.Request('/product/ desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a>
  • 30. # better... <a href="/product/desc/1" onclick="new Ajax.Request(this.href, {asynchronous:true, evalScripts:true}); return false;">View description</a>
  • 31. It's not possible to do that with link_to_remote
  • 32. <% @products.each do |product| %> <%= link_to_remote 'View description', :controller => 'product', :action => 'desc', :id => @product.id %> <% end %>
  • 33. <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a>
  • 34. <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a> <a href="#" onclick="new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;">View description</a>
  • 35. Getting started
  • 36. The UJS Plugin ★ A plugin to aid unobtrusive scripting with Rails ★ Allows you to define behaviours via CSS selectors ★ Keeps script in an external, cacheable files ★ www.ujs4rails.com - check it out!
  • 37. Remote Links
  • 38. <ul id="outline"> <% @items.each do |item| %> <li><%= link_to :action => 'more', :id => @item.id %></li> <% end %> </ul>
  • 39. <ul id="outline"> <% @items.each do |item| %> <li><%= link_to :action => 'more', :id => @item.id %></li> <% end %> </ul> <% apply_behaviour '#outline a:click', 'new Ajax.Request(this.href); return false;' %>
  • 40. <ul id="outline"> <% @items.each do |item| %> <li><%= link_to :action => 'more', :id => @item.id %></li> <% end %> </ul> <% apply_behaviour '#outline a', make_remote_link %>
  • 41. What about links with side-effects?
  • 42. Links should never have side effects
  • 43. Use a button <%= button_to 'Remove from basket', :method => :delete %>
  • 44. <form class="button-to" action="/basket/2" method="post"> <input type="hidden" name="_method" value="delete" /> <input type="submit" value="Remove from basket" /> </form>
  • 45. Then attach the behaviour <%= button_to 'Remove from basket', :method => :delete %> <% apply_behaviour 'form.button-to', make_remote_form %>
  • 46. Hijacking forms
  • 47. <%= form_tag :url => entries_url, :id => 'comment' %> <%= text_field :name %> <%= text_field :email %> <%= text_area :comment %> <%= submit_tag 'Post comment' %> <%= end_form_tag %>
  • 48. <%= form_tag :url => entries_url, :id => 'comment' %> <%= text_field :name %> <%= text_field :email %> <%= text_area :comment %> <%= submit_tag 'Post comment' %> <%= end_form_tag %> <% apply_behaviour '#comment:submit', 'new Ajax.Request(this.action, { parameters : Form.serialize(this)}); return false;' %>
  • 49. <%= form_tag :url => entries_url, :id => 'comment' %> <%= text_field :name %> <%= text_field :email %> <%= text_area :comment %> <%= submit_tag 'Post comment' %> <%= end_form_tag %> <% apply_behaviour '#comment', make_remote_form %>
  • 50. A Case Study Sneakr.com: A Web 2.0, Ajax Trainer Shop
  • 51. routes.rb map.resource :products map.resource :basket, :controller => 'basket', :collection => {:clear => :post}
  • 52. The Product Controller class ProductController < ApplicationController def index # show all products @products = Product.find :all end def show # show the details of a product @product = Product.find params[:id] end end
  • 53. index.rhtml <div id="catalogue"> <%= render :partial => 'products' %> </div> <div id="basket"> <%= render :partial => 'basket' %> </div>
  • 54. _products.rhtml <ul> <% @products.each do |product| %> <li id="<%= product.id %>_prod"> <%= link_to product.name, product_url(product) %> <%= product.description %> </li> <% end %> </ul>
  • 55. show.rhtml <h1><%= @product.name %></h1> <p><%= image_tag @product.photo.public_filename %></p> <p><%= @product.description %></p> <p><%= button_to 'Add To Basket', basket_url (@product), :method => :put %></p>
  • 56. The Basket Controller class BasketController < ApplicationController def update @product = Product.find params[:id] @basket << @product redirect_to products_url end end
  • 57. We're done!
  • 58. now to add the Ajax
  • 59. <% apply_behaviours do on '#catalogue li', make_draggable(:revert => true) on '#basket', make_drop_receiving( :url => basket_url, :with => "'_method=put&id=' + encodeURIComponent(element.id)" ) end %>
  • 60. <% apply_behaviours do on '#catalogue li', make_draggable(:revert => true) on '#basket', make_drop_receiving( :url => basket_url, :with => "'_method=put&id=' + encodeURIComponent(element.id)" ) end %>
  • 61. So how do we deal with this on the server-side?
  • 62. respond_to
  • 63. class BasketController < ApplicationController def update @product = Product.find params[:id] @basket << @product redirect_to products_url end end
  • 64. class BasketController < ApplicationController def update @product = Product.find params[:id] @basket << @product respond_to do |type| type.html { redirect_to products_url } type.js end end end
  • 65. update.rjs page.replace_html 'basket', :partial => 'basket'
  • 66. Take a look for yourself... http://www.danwebb.net/railsconf2006/ujs_shopping.zip
  • 67. We have no control over the environment our JavaScript runs in
  • 68. Code defensively
  • 69. The path to enlightenment ★ Write a working application using semantic HTML ★ Style it with CSS ★ Write JavaScript that 'hijacks' the page elements to enhance the UI ★ Learn JavaScript and DOM Scripting
  • 70. Further Reading ★ The JavaScript articles on A List Apart (alistapart.com) ★ Unobtrusive Scripting by Christian Heilmann (onlinetools.org) ★ Jeremy Keith's presentations, book and articles (domscripting.com) ★ Google it!
  • 71. Upcoming in UJS ★ Improved testing (custom assertions) ★ Improved debugging ★ More behaviour helpers ★ More tutorials on ujs4rails.com
  • 72. And finally... <%= apply_behaviour @products, make_draggable %>
  • 73. <% div_for @product, :behavior => make_draggable do %> <h2><%= @product.name %></h2> <p><%= @product.description %></p> <% end %>
  • 74. Questions?

×