310 likes | 419 Views
Review: Partials. Reusable chunk of a viewe.g., one line of a Student tablee.g., form to display/capture Student info that can be used as part of Edit, Show, Create,...file naming convention: the partial foo for model bar is in app/views/bar/_foo.rhtml by default, partial expects a local (not in
E N D
1. Intro to AJAX & Railsallyourcode@gmail.com Ito learn about april 11-12 CSUA hackathon CS 98-10/CS 198-10
Web 2.0 Programming Using Ruby on Rails
Armando Fox
2. Review: Partials Reusable chunk of a view
e.g., one line of a Student table
e.g., form to display/capture Student info that can be used as part of Edit, Show, Create,...
file naming convention: the partial foo for model bar is in app/views/bar/_foo.rhtml
by default, partial expects a local (not instance) variable whose name matches partial’s filename
in “caller”: render :partial => 'student'
sets local student from instance var @student
override with (e.g.) :object => @other_student
useful when calling same partial from different views
in partial: use student (not @student)
3. Review: collection of partials Common idiom:
@students.each do |student|render :partial => 'student'
Captured by:
render :partial => 'student', :collection => @good_students
local student in partial is set successively to each value in @good_students (using each)
other options allow specifying “divider” template
Can also pass local variables
:locals => {:stu_info => @other_info}
4. Ajax: Web 1.0 ? Web 2.0 Web 1.0 (“old world”) GUI: click ? page reload
Web 2.0: click ? page updates in place
also timer-based interactions, drag-and-drop, animations, etc.
How is this done?
Document Object Model (c.1998, W3C) represents document as a hierarchy of elements
JavaScript (c.1995; now ECMAScript) makes DOM available programmatically, allowing modification of page elements after page loaded
XMLHttpRequest (MSIE 5, c.2000; others, c.2002) allows async HTTP transactions decoupled from page reload
5. DOM & JavaScript:Document = tree of objects A platform-independent (?) hierarchical object model representing HTML or XML doc
part of a separate standards effort; in practice, implementations vary
Exposed to JavaScript interpreter
Inspect DOM element value/attribs
Change value/attribs ? redisplay
Key concept: every element can be given a unique ID (separate from its name), which can be referenced from within JavaScript handlers
Can also “walk the DOM” to find an element but it’s not recommended
6. JavaScript A dynamic, interpreted, object-oriented browser-side scripting language
JavaScript interpreter is part of the browser
embedded in most browsers since c.1998
Browser exposes some of its rendering engine & attributes to JavaScript environment
eg, window, document objects
eg, XmlHttpRequest browser method
JavaScript handlers allow associating JavaScript functions with events on DOM elements
e.g., onClick, onMouseOver, onFocus...
7. DOM: Attributes Attributes: the state of a DOM element
current text typed into a text box?
current selection of a dropdown menu?
A common idiom:
m = document.getElementById("course_menu");
menuText=m.options[m.selectedIndex].value;
In an element’s handler, this is set to the object representing the element
<a href="#" onClick="this.innerHTML='Presto!'">Click me</a>
8. Handlers on Buttons & Links A good summary of recognized handlers:http://www.chipchapin.com/WebTools/JavaScript/example1-04.html
a few common ones: onClick, onSubmit, onChange, onLoad/onUnload, onChange
not every handler applies to every HTML element
What about links & buttons?
if handler returns false, no other action taken
otherwise, other actions follow handler
example: client-side form validation
Handler code can be inline or functions...let’s see an example
9. XMLHttpRequest (aka xhr) separates HTTP request/response from page render
Web 1.0: every HTTP request renders page anew
Web 2.0: HTTP requests can occur “in the background” after page is rendered
XHR’s do not re-render page, but can selectively change DOM objects
Requires support in browser
Originally: Microsoft IE 5 (c.2000), now in all major browsers (some minor incompatibilities)
10. So: What’s AJAX? Asynchronous JavaScript And XML
Early showcase app: Google Maps
Recipe (to a zeroth order):
attach JavaScript handlers to various events on browser objects (so you get notified when user interacts with page elements)
in callback, inspect/modify DOM elements and optionally do an asynchronous HTTP req. to server
on server response, pass result to yet another JavaScript function that will monkey with DOM again
Rails integrates seamless Ajax support
Prototype to deal with cross-browser issues, common Ajax functionality, etc.
Script.aculo.us, a JavaScript library of visual effects
11. XHR (simplified) r=XmlHttpRequest.new
r.open(method,URL,async)
method ? GET,POST,HEAD,PUT,DELETE...
async: should script continue without waiting?
r.send(request_content)
r.abort()
Callbacks during XHR processing
r.onReadyStateChange = (Javascript function reference)
function inspects r.readyState ? uninitialized,open, sent,receiving,loaded
r.status contains HTTP status of response
r.responseText contains response content string
12. Javascript handlers (simplified) <input type="button" value="Extra Contact Info..." onClick="Effect.toggle('contact','slide',{});" />
example: onClick handler called when form button is clicked
handler calls JavaScript function toggle in Effect class (which happens to be part of script.aculo.us)
for this function, first arg refers to a DOM element ID, second arg refers to name of visual effect to apply, third arg is hash of optional additional params (empty in this call)
function toggles whether contact element is hidden or showing, and uses slide effect to transition between!
compare simpler:
onClick="e=getElementById('contact'); if e.style.display=='none' {e.style.display='inline'; } else { e.style.display='none'; }"
13. Other common uses of “inline” JavaScript Rails provides Javascript helpers that generate Javascript code in your RHTML templates
Example: “Are you sure?” dialog
<%= button_to("Remove", {:action=>'destroy_something',:id=>@voucher}, {:confirm=>"Are you sure?"}) %>
Note careful use of braces!!
<form method="post" action="/vouchers/remove_voucher/707" class="button-to"><div> <input onclick="return confirm('Are you sure?');" type="submit" value="Remove"/></div></form>
Example: popup alert box: <%= alert('You suck.') %>
14. Inline JS, continued Example: “Processing...” form button:
<%= submit_tag 'Submit', {:disable_with => 'Please Wait'} %>
produces:
<input name="commit" onclick="this.disabled=true;this.value='Please Wait';this.form.submit();" type="submit" value="Download Report in Excel format" />
15. Updating page content using JavaScript General strategy
“containerize” the updatable content in a DOM element (can always use <span>)
use the innerHTML property to modify the HTML inside the element tags
A really trivial example....
16. prototype Javascript library captures common xhr usage Common pattern: encapsulate xhr call to update-in-place some content on page
URL to call on remote server
DOM ID tag of element whose “inner HTML” (i.e. content) should be replaced with result
Example: do something in response to immediate user input (e.g. auto-completion)
DOM ID tag(s) of element(s) on page to monitor for user input (text fields, menus, whole form, etc.)
URL to call on remote server when content changes (passes new content as argument)
17. prototype (prototypejs.org) & Scriptaculous (script.aculo.us) prototype also hides some cross-browsers differences in Javascript/XHR implementations
eg, portable $() function to dereference DOM element by its ID
$("submit_btn").disabled = true;
var AjaxOpts = {
method: "get",
parameters: "id=3&user=" + $("usrname").value,
onComplete: displayResponse };
var AjaxReq = new Ajax.Request (url,AjaxOpts);
function displayResponse() {...}
script.aculo.us encapsulates various visual effects obtained by careful manipulation of DOM properties
e.g. blind up, blind down to show/hide some text
18. Putting it together: AJAX the Rails way Event handlers are just controller methods!
Rails wrappers around prototype library functions marshal arguments & do XHR call
Method can tell how it was called by using respond_to do |wants| wants.js { ... }
But may be cleaner to have separate controller methods for xhr vs. regular forms
19. Getting Results from the Server, the Rails way Typically, results replace content of an HTML element
Remember you can “elementize” (almost) any arbitrary chunk using <span id="foo"> or <div id="foo">
Controller method can use render :partial to produce a result
Typical example: table with collapsible entries
or render :text to send raw content back
20. An example: rhtml & JS <% @donations.each do d %>
<div id="donation_<%=d.id-%>">
....
<%= link_to_remote 'Mark Sent',
:update=>"donation_#{d.id}",
:url=>{:action=>'mark_ltr_sent', :id=>d.id} %>
The rendered page looks like this:
<div id='donation_751'>
<a href="#" onclick="new Ajax.Updater('donation_751', '/donation/mark_ltr_sent/751',
{asynchronous:true, evalScripts:true}); return false;">Mark Sent</a>
21. The controller method... def mark_ltr_sent
begin
t = Donation.find(params[:id])
c = Customer.find(logged_in_id)
t.update_attributes(:letter_sent => now,
:processed_by => c.id)
render :text => "#{Time.now} by #{c.login}"
rescue
render :text => "(ERROR)"
end
end
22. Listening For Events Not surprisingly, Rails lets you listen at the element or form level
observe_field('student[last_name]',
:url => {:controller=>'students', :action=>'lookup_by_lastname'}, :update=>'lastname_completions')
when student[last_name] field changes, call method lookup_by_lastname in StudentController with new field value
returned text from controller method will replace the “inner contents” of element ID lastname_completions
typically using render :partial or render :text
23. Listening on a Whole Form observe_form('student_form',
:url => {:controller => 'students', :action => 'process_form'},
:update => 'student_info_panel')
When any element of student_form changes, call process_form method in StudentsController, marshalling all elements into params[]
24. State changes & visual effects Additional arguments allow specifying callbacks
states: server contacted, waiting, receiving, done
link_to_remote('Show article',:update => 'article_content',:url => {:action => 'get_article_text',:id => article},:before => "Element.show('spinner')",:complete => "Element.hide('spinner')",404 => alert("Article text not found!"))
25. Important to remember... All of these types of JS “helpers” expand to JavaScript code in your .rhtml template
1. <%= javascript_include_tag :defaults %>
in RHTML template expands to:
<script src="/javascripts/prototype.js" type="text/javascript"></script>
...various other script files loaded...
<script src="/javascripts/application.js" type="text/javascript"></script>
2. <%= link_to_remote ... %> in RHTML expands to more JS
3. Browser loads page with all expansions
4. User clicks link, invoking JavaScript function
5. Your app server gets called, returns something
6. That something replaces content of tag ID in :update argument
26. script.aculo.us Captures common patterns for visual effects on Web pages
e.g.: “blind down/blind up” to hide/show an element
button_to_function "Extra Contact Info...", visual_effect(:toggle_slide, :contact)
One strategy: preload all page content, then selectively show
Another strategy: lazily fetch content using XHR
link_to_remote('Show article',:url => {:action => 'get_article_text',:id => article},:complete => visual_effect(:toggle_slide,'article_content', :duration=>0.5),404 => alert("Article text not found!"),:failure => alert("Error: "+request.status))
27. Graceful Fallback to Web 1.0 What if AJAX support not available in user’s browser?
Specifying a fallback in the AJAX tags
:html => options
how does view “know” whether to use it or not?
28. How to think of cool GUI tasks in terms of AJAX... “Auto-completion” of a text field
“Update now” button
Periodically polling for updates (periodically_call_remote)
Repopulate popup menus constrained to choices in other menus
One approach: use AJAX call to re-render the whole <select> item
29. Remote Javascript (RJS) a/k/a “writing JS without writing JS” a/k/a “there is no spoon”
idea: when rendering response to XHR call, instead of text (HTML), return more JS that will execute at client side and do something.
helper methods provided in Ruby that generate the necessary JS
30. Simple RJS example respond_to do |wants|
wants.js do
render :update do |page|
page['some_div'].hide
page['other_div'].replace_html('old','new')
page.alert "Something's wrong"
page.call('some_function')
end
end
Even better: put the above code in a view template action.js.rjs
31. The dark side of AJAX, RJS, etc. Lots of layers of code; can be hard to debug
Browsers tend to fail silently when they choke on JS-related errors
View JS console/log in most decent browsers
install Firefox Developer Toolbar & Firebug
On the plus side...
eminently more maintainable
probably more robust and browser-neutral