- XML Reference Guide
- Overview
- What Is XML?
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Table of Contents
- The Document Object Model
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- DOM and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- Implementations
- DOM and JavaScript
- Using a Repeater
- Repeaters and XML
- Repeater Resources
- DOM and .NET
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Downloads
- DOM and C++
- DOM and C++ Resources
- DOM and Perl
- DOM and Perl Resources
- DOM and PHP
- DOM and PHP Resources
- DOM Level 3
- DOM Level 3 Core
- DOM Level 3 Load and Save
- DOM Level 3 XPath
- DOM Level 3 Validation
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Implementations
- The Simple API for XML (SAX)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- SAX and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- SAX and .NET
- Informit Articles and Sample Chapters
- SAX and Perl
- SAX and Perl Resources
- SAX and PHP
- SAX and PHP Resources
- Validation
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Document Type Definitions (DTDs)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XML Schemas
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- RELAX NG
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Schematron
- Official Documentation and Implementations
- Validation in Applications
- Informit Articles and Sample Chapters
- Books and e-Books
- XSL Transformations (XSLT)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XSLT in Java
- Java in XSLT Resources
- XSLT and RSS in .NET
- XSLT and RSS in .NET Resources
- XSL-FO
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XPath
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XML Base
- Informit Articles and Sample Chapters
- Official Documentation
- XHTML
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XHTML 2.0
- Documentation
- Cascading Style Sheets
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XUL
- XUL References
- XML Events
- XML Events Resources
- XML Data Binding
- Informit Articles and Sample Chapters
- Books and e-Books
- Specifications
- Implementations
- XML and Databases
- Informit Articles and Sample Chapters
- Books and e-Books
- Online Resources
- Official Documentation
- SQL Server and FOR XML
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Implementations
- Service Oriented Architecture
- Web Services
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Creating a Perl Web Service Client
- SOAP::Lite
- Amazon Web Services
- Creating the Movable Type Plug-in
- Perl, Amazon, and Movable Type Resources
- Apache Axis2
- REST
- REST Resources
- SOAP
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- SOAP and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- WSDL
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- UDDI
- UDDI Resources
- XML-RPC
- XML-RPC in PHP
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Ajax
- Asynchronous Javascript
- Client-side XSLT
- SAJAX and PHP
- Ajax Resources
- JSON
- Ruby on Rails
- Creating Objects
- Ruby Basics: Arrays and Other Sundry Bits
- Ruby Basics: Iterators and Persistence
- Starting on the Rails
- Rails and Databases
- Rails: Ajax and Partials
- Rails Resources
- Web Services Security
- Web Services Security Resources
- SAML
- Informit Articles and Sample Chapters
- Books and e-Books
- Specification and Implementation
- XML Digital Signatures
- XML Digital Signatures Resources
- XML Key Management Services
- Resources for XML Key Management Services
- Internationalization
- Resources
- Grid Computing
- Grid Resources
- Web Services Resource Framework
- Web Services Resource Framework Resources
- WS-Addressing
- WS-Addressing Resources
- WS-Notifications
- New Languages: XML in Use
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Google Web Toolkit
- GWT Basic Interactivity
- Google Sitemaps
- Google Sitemaps Resources
- Accessibility
- Web Accessibility
- XML Accessibility
- Accessibility Resources
- The Semantic Web
- Defining a New Ontology
- OWL: Web Ontology Language
- Semantic Web Resources
- Google Base
- Microformats
- StructuredBlogging
- Live Clipboard
- WML
- XHTML-MP
- WML Resources
- Google Web Services
- Google Web Services API
- Google Web Services Resources
- The Yahoo! Web Services Interface
- Yahoo! Web Services and PHP
- Yahoo! Web Services Resources
- eBay REST API
- WordML
- WordML Part 2: Lists
- WordML Part 3: Tables
- WordML Resources
- DocBook
- Articles
- Books and e-Books
- Official Documentation and Implementations
- XML Query
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XForms
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Resource Description Framework (RDF)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Topic Maps
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation, Implementations, and Other Resources
- Rich Site Summary (RSS)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Simple Sharing Extensions (SSE)
- Atom
- Podcasting
- Podcasting Resources
- Scalable Vector Graphics (SVG)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- OPML
- OPML Resources
- Summary
- Projects
- JavaScript TimeTracker: JSON and PHP
- The Javascript Timetracker
- Refactoring to Javascript Objects
- Creating the Yahoo! Widget
- Web Mashup
- Google Maps
- Indeed Mashup
- Mashup Part 3: Putting It All Together
- Additional Resources
- Frequently Asked Questions About XML
- What's XML, and why should I use it?
- What's a well-formed document?
- What's the difference between XML and HTML?
- What's the difference between HTML and XHTML?
- Can I use XML in a browser?
- Should I use elements or attributes for my document?
- What's a namespace?
- Where can I get an XML parser?
- What's the difference between a well-formed document and a valid document?
- What's a validating parser?
- Should I use DOM or SAX for my application?
- How can I stop a SAX parser before it has parsed the entire document?
- 2005 Predictions
- 2006 Predictions
- Nick's Book Picks
We've done all the hard work, such as accessing the database, so now we can start adding bells and whistles.
The most obvious of those bells is the web-technique-du-jour, Ajax, which enables you to replace only part of a web page with new information from the server. If you have read previous sections on Ajax in general and Ajax and PHP, and you know that Ajax can be a significant challenge to pull off.
Fortunately, Ajax is just one more thing Rails makes almost childishly easy.
In this section, we'll Ajax-enable the SurveySays
utility for
entering Question
s into the system, providing the ability
to add and delete questions without reloading the "list" page. We'll also look
at "partials", which enable you to easily reuse code and display logic.
As you recall, we currently have a template, or view, for the list
action.
(If you don't recall, go back and read previous sections.)
This view lists the existing questions, along with links that enable the user to edit
or delete them. It also includes a form for entering new questions.
To start adding Ajax capabilities, we'll change the "delete" links slightly by making
changes to the app/views/question/list.rhtml
file:
<html> <head> <title>Questions, questions questions</title> <%= javascript_include_tag "prototype" %> </head> <body> <h2>Existing questions</h2> <div id="listOfQuestions"> <% @questions.each do |@thisQuestion| %> <%= @thisQuestion.questionText %> ( <%= link_to("Change", :action => "edit", :id => @thisQuestion.id) %> ) ( <%= link_to_remote("Delete", :update => "listOfQuestions", :url => {:action => "destroy", :id => @thisQuestion.id}) %> ) <br /> <% end %> </div> <br /> <form method="post" action="addNewQuestion"> Add a new question: <%= text_field("newQuestion", "questionText") %> <input type="submit" value="Save Question"> </form> </body> </html>
Starting at the top, remember that Ajax is handled using JavaScript, so we first have to
import the appropriate JavaScript code. The "prototype" library ships with Rails, so all you have to do is
include it. Next, our goal is to change part of the page, so we'll define what part of the page were changing
by creating a div
called listOfQuestions
. Finally, we use
link_to_remote
instead of link_to
to tell Rails to add the
Ajax code to the link.
The parameters we are using are as follows. First, we list the text for the link. Next the :update
parameter lists the div
to change. Finally, the :url
is the code to actually call. As before, we are calling the destroy
action for question @thisQuestion.id
.
The page itself doesn't show any outward changes:
You can do a "view source" to see how the actual HTML changes, but the quickest way to see the changes to click the "delete" link for an existing question.
That's not quite what we wanted, of course, so let's see what happened.
We told Rails that we wanted to replace the contents of the listOfQuestions
div
with the output of the destroy
action. To see what that is,
let's look at the question_controller.rb
:
... redirect_to(:action => "list") else render_text "Sorry, no can do." end end def destroy thisQuestion = Question.find(@params["id"]) if thisQuestion.destroy redirect_to(:action => "list") else render_text "Sorry, no can do." end end end
First we are finding and deleting the specific question, but then
we defer to the list
action, which outputs the entire page
including the header and the form at the bottom. All of this information winds up in the
listOfQuestions
div
, giving the illusion of
a page within a page. (Actually, it's no illusion. There really is
a page within the page.)
What we really need is a way to display only part of the page. In this case, it is a part of the page that displays the individual list items. In Rails, this is called a "partial". Partials are small snippets you can reuse in various places. We'll use them to display the questions.
Start by creating a new file called _questionList.rhtml
.
Note that the leading underscore is what designates the file as a partial, so make sure you don't
forget to add it. Save the file in the app/views/question
directory. Add the following code:
<%= @thisQuestion.questionText %> ( <%= link_to("Change", :action => "edit", :id => @thisQuestion.id) %> ) ( <%= link_to_remote("Delete", :update => "listOfQuestions", :url => {:action => "destroy", :id => @thisQuestion.id}) %> ) <br />
Now we need to tell Rails to render the partial on the page:
... <h2>Existing questions</h2> <div id="listOfQuestions"> <% @questions.each do |@thisQuestion| %> <%= render_partial "questionList", @thisQuestion %> <% end %> </div> <br /> <form method="post" action="addNewQuestion"> ...
First we provide the name of the partial we want to use (note that we do not reference the underscore here, just in the filename), and then the data we are passing into it. With these changes in place, you should not see any change to the actual display:
Of course you will almost always use this in the context of looping through data of some sort, so fortunately Rails provides a way to do this simply:
... <body> <h2>Existing questions</h2> <div id="listOfQuestions"> <%= render_collection_of_partials "questionList", @questions %> </div> <br /> <form method="post" action="addNewQuestion"> ...
The renderer_collection_of_partials
function again takes the name of the
partial to use, but in this case rather than a single object, it takes
an array through which Rails should loop.
But we still have to tell the partial what values it's working with:
<% @thisQuestion = questionList %> <%= @thisQuestion.questionText %> ( <%= link_to("Change", :action => "edit", :id => @thisQuestion.id) %> ) ( <%= link_to_remote("Delete", :update => "listOfQuestions", :url => {:action => "destroy", :id => @thisQuestion.id}) %> ) <br />
We are simply telling Rails to associate the @thisQuestion
variable with successive items in the array passed into the questionList
partial.
But where does the array actually come from? It comes from the controller,
which creates the array before actually executing the list
view.
That works fine for the list
action, but we need a way to simply
display the questions all by themselves. Specifically, we need an action
that does it. We'll call the action showQuestions
.
Start with the view, showQuestions.rhtml
. It only needs
one line:
<%= render_collection_of_partials "questionList", @questions %>
Now we need to create the action in the controller so that we can
create the actual @questions
array:
... def destroy thisQuestion = Question.find(@params["id"]) if thisQuestion.destroy redirect_to(:action => "list") else render_text "Sorry, no can do." end end def showQuestions @questions = Question.find_all end end
Before we move on, let's test the new action. Point your browser to:
http://localhost:3000/question/showQuestions
You should see a list of questions:
Okay now that we know that that works, and that it gives us what we need to put into the
div
, we need to create the action that calls showQuestions
:
... def destroy thisQuestion = Question.find(@params["id"]) if thisQuestion.destroy redirect_to(:action => "list") else render_text "Sorry, no can do." end end def deleteQuestion thisQuestion = Question.find(@params["id"]) if thisQuestion.destroy redirect_to(:action => "showQuestions") else render_text "Sorry, no can do." end end def showQuestions @questions = Question.find_all end end
Essentially this is the same as destroy
, but now you can
just replace the list, rather than the whole page. First add it to the
_questionList.rhtml
:
<% @thisQuestion = questionList %> <%= @thisQuestion.questionText %> ( <%= link_to("Change", :action => "edit", :id => @thisQuestion.id) %> ) ( <%= link_to_remote("Delete", :update => "listOfQuestions", :url => {:action => "deleteQuestion", :id => @thisQuestion.id}) %> ) <br />
Now if you open your browser to the list and click a "Delete" link, you should see just the list of questions change.
That takes care of links, but what about forms? After all,
we do a lot of the same data manipulation techniques with forms
as we do with links. Well, Rails provides a very similar in way of dealing with this issue
for forms. Start by adjusting the list
to create a new form:
... <div id="listOfQuestions"> <%= render_collection_of_partials "questionList", @questions %> </div> <br /> <%= form_remote_tag(:update => "listOfQuestions", :url => { :action => :addNewQuestion } ) %> Add a new question: <%= text_field("newQuestion", "questionText") %> <input type="submit" value="Save Question"> <%= end_form_tag %> </body> </html>
The form here is virtually identical to its predecessor, with the exception of the
Ajax enabling code, which works exactly the same way as link_to_remote
.
In fact, it is so similar that if you simply try it out now, you'll get the same reaction
we got at the beginning of this section: a doubling of the page. What we need to do
is apply the same techniques to the addNewQuestion
action.
In fact, what we need is for this action to simply output the new question
so that we can add it to the existing list. In the controller, alter the
addNewQuestion
action so that it uses the partial:
... render_text "Sorry, no can do." end end def addNewQuestion thisQuestion = Question.new thisQuestion.attributes = @params["newQuestion"] if thisQuestion.save render_text (render_partial "questionList", thisQuestion) else render_text "Sorry, no can do." end end ...
But if we submit it, we don't get quite what we want:
In a good example of the computer doing what you tell it to and not what you want it to
do, Rails has told the browser to replace the entire contents of the div
with the new
content. But what we really want is for the new text to be added at the bottom of the
list. We can do that by setting the position on the form itself:
... ... <br /> <%= form_remote_tag(:update => "listOfQuestions", :url => { :action => :addNewQuestion }, :position => "bottom") %> Add a new question: <%= text_field("newQuestion", "questionText") %> <input type="submit" value="Save Question"> <%= end_form_tag %> </body> </html>
Now if you try it, the new question should get added at the bottom of the div:
You can set :position
values of top
and
bottom
, which add the new content inside the div
,
or before
and after
, which add the new content outside the
div
.
There are plenty of other bells and whistles, but this should hold us for now.