Date post: | 19-Jan-2015 |
Category: |
Documents |
Upload: | sampetruda |
View: | 797 times |
Download: | 8 times |
UC Berkeley
Intro to AJAX & [email protected] Ito learn
about april 11-12 CSUA hackathon
CS 98-10/CS 198-10
Web 2.0 Programming Using Ruby on Rails
Armando Fox
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)
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}
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?1. Document Object Model (c.1998, W3C) represents document
as a hierarchy of elements
2. JavaScript (c.1995; now ECMAScript) makes DOM available programmatically, allowing modification of page elements after page loaded
3. XMLHttpRequest (MSIE 5, c.2000; others, c.2002) allows async HTTP transactions decoupled from page reload
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
QuickTime™ and aTIFF (Uncompressed) decompressor
are needed to see this picture.
<input type="text" name="phone_number" id="phone_number"/><script type="text/javascript"> var phone = document.getElementById('phone_number'); phone.value='555-1212'; phone.disabled=true; document.images[0].src="http://.../some_other_image.jpg";</script>
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...
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>
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
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)
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
XHR (simplified)
r=XmlHttpRequest.newr.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
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'; }"
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.') %>
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" />
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....
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)
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
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
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
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>
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
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
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[]
State changes & visual effects
• Additional arguments allow specifying callbacks– states: server contacted, waiting, receiving,
donelink_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!"))
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 JS3. Browser loads page with all expansions4. User clicks link, invoking JavaScript function5. Your app server gets called, returns something6. That something replaces content of tag ID in :update
argument
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 XHRlink_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))
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?
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
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
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') endend
• Even better: put the above code in a view template action.js.rjs
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