Dan Webb Presentation

1,293 views
1,183 views

Published on

Published in: Business, Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,293
On SlideShare
0
From Embeds
0
Number of Embeds
28
Actions
Shares
0
Downloads
50
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Dan Webb Presentation

  1. 1. Unobtrusive Ajax With Rails Dan Webb (dan@danwebb.net)
  2. 2. Overview A bit of history ★ What is unobtrusive scripting? ★ The UJS Plugin ★ Casestudy ★ Ranting Upcoming UJS Features ★
  3. 3. The dark days of DHTML
  4. 4. Then web standards arrived
  5. 5. The Client-side Cake Style - CSS Content - (X)HTML
  6. 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. 7. JavaScript got a bad name
  8. 8. It deserved it
  9. 9. Web 2.0
  10. 10. Folksonomies
  11. 11. Massive text boxes
  12. 12. Social software
  13. 13. and Ajax...
  14. 14. JavaScript is trendy again!
  15. 15. Browser support is much better
  16. 16. We learnt our lessons from the DHTML days
  17. 17. What did we learn?
  18. 18. Unobtrusive DOM Scripting
  19. 19. It's an approach to browser UI design
  20. 20. It's about separating content and style from behaviour
  21. 21. Behaviour: A new layer for the client-side cake Behaviour - JavaScript Style - CSS Content - (X)HTML
  22. 22. It's enhancing a working application
  23. 23. so it degrades gracefully when things don't work
  24. 24. It's not just about putting JavaScript in a different file
  25. 25. It's not rocket science
  26. 26. It's not the 'Rails Way'
  27. 27. An example: link_to_remote
  28. 28. <%= link_to_remote 'View description', :controller => 'product', :action => 'desc', :id => @product.id %>
  29. 29. <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/ desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a>
  30. 30. # better... <a href=quot;/product/desc/1quot; onclick=quot;new Ajax.Request(this.href, {asynchronous:true, evalScripts:true}); return false;quot;>View description</a>
  31. 31. It's not possible to do that with link_to_remote
  32. 32. <% @products.each do |product| %> <%= link_to_remote 'View description', :controller => 'product', :action => 'desc', :id => @product.id %> <% end %>
  33. 33. <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a>
  34. 34. <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a> <a href=quot;#quot; onclick=quot;new Ajax.Request('/product/desc/1', {asynchronous:true, evalScripts:true}); return false;quot;>View description</a>
  35. 35. Getting started
  36. 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. 37. Remote Links
  38. 38. <ul id=quot;outlinequot;> <% @items.each do |item| %> <li><%= link_to :action => 'more', :id => @item.id %></li> <% end %> </ul>
  39. 39. <ul id=quot;outlinequot;> <% @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. 40. <ul id=quot;outlinequot;> <% @items.each do |item| %> <li><%= link_to :action => 'more', :id => @item.id %></li> <% end %> </ul> <% apply_behaviour '#outline a', make_remote_link %>
  41. 41. What about links with side-effects?
  42. 42. Links should never have side effects
  43. 43. Use a button <%= button_to 'Remove from basket', :method => :delete %>
  44. 44. <form class=quot;button-toquot; action=quot;/basket/2quot; method=quot;postquot;> <input type=quot;hiddenquot; name=quot;_methodquot; value=quot;deletequot; /> <input type=quot;submitquot; value=quot;Remove from basketquot; /> </form>
  45. 45. Then attach the behaviour <%= button_to 'Remove from basket', :method => :delete %> <% apply_behaviour 'form.button-to', make_remote_form %>
  46. 46. Hijacking forms
  47. 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. 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. 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. 50. A Case Study Sneakr.com: A Web 2.0, Ajax Trainer Shop
  51. 51. routes.rb map.resource :products map.resource :basket, :controller => 'basket', :collection => {:clear => :post}
  52. 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. 53. index.rhtml <div id=quot;cataloguequot;> <%= render :partial => 'products' %> </div> <div id=quot;basketquot;> <%= render :partial => 'basket' %> </div>
  54. 54. _products.rhtml <ul> <% @products.each do |product| %> <li id=quot;<%= product.id %>_prodquot;> <%= link_to product.name, product_url(product) %> <%= product.description %> </li> <% end %> </ul>
  55. 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. 56. The Basket Controller class BasketController < ApplicationController def update @product = Product.find params[:id] @basket << @product redirect_to products_url end end
  57. 57. We're done!
  58. 58. now to add the Ajax
  59. 59. <% apply_behaviours do on '#catalogue li', make_draggable(:revert => true) on '#basket', make_drop_receiving( :url => basket_url, :with => quot;'_method=put&id=' + encodeURIComponent(element.id)quot; ) end %>
  60. 60. <% apply_behaviours do on '#catalogue li', make_draggable(:revert => true) on '#basket', make_drop_receiving( :url => basket_url, :with => quot;'_method=put&id=' + encodeURIComponent(element.id)quot; ) end %>
  61. 61. So how do we deal with this on the server-side?
  62. 62. respond_to
  63. 63. class BasketController < ApplicationController def update @product = Product.find params[:id] @basket << @product redirect_to products_url end end
  64. 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. 65. update.rjs page.replace_html 'basket', :partial => 'basket'
  66. 66. Take a look for yourself... http://www.danwebb.net/railsconf2006/ujs_shopping.zip
  67. 67. We have no control over the environment our JavaScript runs in
  68. 68. Code defensively
  69. 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. 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. 71. Upcoming in UJS Improved testing (custom assertions) ★ Improved debugging ★ More behaviour helpers ★ More tutorials on ujs4rails.com ★
  72. 72. And finally... <%= apply_behaviour @products, make_draggable %>
  73. 73. <% div_for @product, :behavior => make_draggable do %> <h2><%= @product.name %></h2> <p><%= @product.description %></p> <% end %>
  74. 74. Questions?

×