UC Berkeley Intro to AJAX

Post on 19-Jan-2015

797 views 8 download

Tags:

description

 

Transcript of UC Berkeley Intro to AJAX

UC Berkeley

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

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