Sep 12, 2014
ABOUT ME•張文鈿 a.k.a ihower
• http://ihower.idv.tw/blog/
• http://twitter.com/ihower
•和多 Rails developer
• http://handlino.com
• http://registrano.com
2009年5月19日星期二
AGENDA
• 1. some useful helper technique
• 2. handicraft_helper plugin
• 3. handicraft_ujs plugin
• 4. some UI design patterns implementation
2009年5月19日星期二
EXAMPLE CODE
1. download 2. rake dev:build3. script/server
all demo code is here:
http://github.com/ihower/handicraft-interfaces
2009年5月19日星期二
1. USEFUL HELPER TECHNIQUES
2009年5月19日星期二
CONTENT_FOR
# /layouts/application.html.erb
<html> <head>
<%= yield :page_javascript %> </head><body> <%= yield %>
<div id=”sidebar”><%= yield :sidebar %>
<div></body></html>
# foobar.html.erb<p>fooooooooooooooooooo</p>
<% content_for :page_javascript do %> <script type="text/javascript"> ... </script><% end %>
<p>barrrrrrrrrrrrrrrrrr</p>
A
A
B
B
A
2009年5月19日星期二
PASSING BLOCK PARAMETER
<% link_to_remote :url => contact_path(contact), :method => :get do %> <span class="picture"> <%= image_tag contact.buddy_icon %> </span> <span class="real-name"> <%= contact.full_name %></span> <% if contact.friendly? %> <span class="is-friend">is your friend.</span> <% else %> <span class="no-friend">is not your friend.</span> <% end -%> <span class="nickname"><%= contact.nickname %></span> <% end -%>
<% link_to_remote some_helper(contact), :url => contact_path(contact), :method => :get %>
My Want
Original version, we need a helper
2009年5月19日星期二
PASSING BLOCK PARAMETER
# provide block suppoet def link_to_remote(*args, &block) if block_given? options = args.first || {} html_options = args.second concat( link_to_function(capture(&block), remote_function(options), html_options || options.delete(:html)) ) else name = args.first options = args.second || {} html_options = args.third link_to_function(name, remote_function(options), html_options || options.delete(:html)) end end
Use block_given?, concat and capture(&block)
2009年5月19日星期二
CONTENT_TAGRuby Style, less line of code
<% if some_condition %> <h1>foobar</h1><% end %>
<%= content_tag("h1", "foobar") if some_condition %>
def some_helper( title, options = {} ) content_tag("p", content_tag("span", title, options), :class => 'p_class")end
HTML attributes as helper options
<p class="p_class"><span class="foobar">test</span></p>
2009年5月19日星期二
KNOW RAILS HELPERS
• READ Rails helpers source code
• /actionpack-x.y.z/lib/action_view/helpers/*
2009年5月19日星期二
2. HANDICRAFT HELPER PLUGIN
handlino’s favorite rails helpers
2009年5月19日星期二
PROBLEM AND SOLUTION
• We need complex/logistic HTML in helper, but write a lot HTML string + operators are painful.
• My early solution: use markaby plugin, but it’s slow and yet another dependency.
• current solution idea from Composite&Builder pattern (Refactoring to Patterns book), it’s simple, component and fast.
2009年5月19日星期二
COMPOSITE PATTERN
USAGE: class TagNode include ActionView::Helpers::TagHelper def initialize(name, options = {}) @name = name.to_s @attributes = options @children = [] end def to_s value = @children.each { |c| c.to_s }.join content_tag(@name, value.to_s, @attributes) end def <<(tag_node) @children << tag_node end end
table = TagNode.new('table', table_tag_options)table << thead = TagNode.new(:thead) thead << tr = TagNode.new(:tr, :class => 'odd')thead << tr = TagNode.new(:tr, :class => 'even') table << tbody = TagNode.new('tbody') tbody << tr = TagNode.new('tr', :class => cycle("","odd") )
return table.to_s
2009年5月19日星期二
LIVE DEMO
• render_menu
• render_table (column-oriented table)
• breadcrumb
2009年5月19日星期二
3. HANDICRAFT UJSPLUGIN
a jQuery replacement for Ajax on Rails
2009年5月19日星期二
IDEA 1: JRAILShttp://ennerchi.com/projects/jrails
2009年5月19日星期二
IDEA 2: UNOBTRUSIVEhttp://blog.lawrencepit.com/2008/09/04/unobtrusive-jquery-rails/
Separation of functionality from content
2009年5月19日星期二
MY WANT
• jQuery (write less, do more)
• RJS (in controller, because it keep controller ruby style)
• RESTful (Rails way)
• unobtrusive and accessible (hate inline js everywhere)
2009年5月19日星期二
INLINE JS EVERYWHERE<%= link_to_remote 'ajax', :url => person_path(person), :method => :get %>
<a href="#" onclick="new Ajax.Request('/people/1', {asynchronous:true, evalScripts:true, method:'get', parameters:'authenticity_token=' + encodeURIComponent('uwdvZFNiqzfd/iFOX65Ov9AkxVelIUVSylrWWxb58XM=')}); return false;">ajax</a>
<% remote_form_for @person, person_path(@person), :html => { :method => :put } do |f| %>
<form action="/people/1" id="edit_person_1" method="post" onsubmit="new Ajax.Request('/people/1', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">
2009年5月19日星期二
HANDICRAFT WAY<%= link_to 'ajax', person_path(person), :class => "h-get" %> <a href="/people/1" class="h-get">ajax</a>
<% form_for @person, person_path(@person), :html => { :method => :put, :class => 'h-put' } do |f| %>
<form action="/people/1" class="h-put" id="edit_person_1" method="post">
2009年5月19日星期二
<a href="#" onclick="new Ajax.Request('/people/1', {asynchronous:true, evalScripts:true, method:'get', parameters:'authenticity_token=' + encodeURIComponent('uwdvZFNiqzfd/iFOX65Ov9AkxVelIUVSylrWWxb58XM=')}); return false;">ajax</a>
<a href="/people/1" class="h-get">ajax</a>
<form action="/people/1" id="edit_person_1" method="post" onsubmit="new Ajax.Request('/people/1', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">
<form action="/people/1" class="h-put" id="edit_person_1" method="post">
2009年5月19日星期二
HOW
• JQuery 1.3 live binding
• jQuery metadata plugin
• jQuery form plugin
• jQuery version RJS (extract from jRails plugin)
2009年5月19日星期二
LIVE DEMO
• h-get, h-post, h-put, h-delete
• { update: ‘content’ }
• { callback: ‘handle_json’ }
• { confirm: ‘Are you sure? }
2009年5月19日星期二
4. UI DESIGN PATTERNSIMPLEMENTATION
2009年5月19日星期二
IDEAhttp://designingwebinterfaces.com/explore
2009年5月19日星期二
LIVE DEMO
• Single-Field Inline Edit
• Multi-Field Inline Edit
• Group Edit
• Sortable List
2009年5月19日星期二