21 Oct
Posted by ProCOM
on October 21, 2007 – 1:34 pm - 1,396 views
In this conclusion to a two-part article, you’ll learn how to drag and drop slides in our example slideshow to modify it, and how to filter by category. This article is excerpted from chapter six of the book Ruby on Rails: Up and Running, written by Bruce A. Tate and Curt Hibbs (O’Reilly, 2006; ISBN: 0596101325). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
Drag and Drop Everything (Almost Everything)
We have already displayed a list of thumbnails of all photos that are in the slideshow and enabled the user to drag them around to rearrange their order in the slideshow. Now let’s add a second list of thumbnails, showing all photos that are not being used in the slideshow.
We’ll let the user add a photo to the slideshow by dragging it from the list of unused photos and dropping it onto the slideshow thumbnails. Similarly, we can enable the user to remove photos from the slideshow by dragging its thumbnail from the slideshow and dropping on the unused photos list. Finally, we’ll allow the user to filter the unused photos list by category.
As you might expect, we can accomplish all that in a very small amount of code. We will add a mere 58 lines of Ruby code to the models and controllers, 47 lines to the view templates, and 16 lines to our CSS stylesheet! Figure 6-4 gives you a preview of how this is going to look when we’re done.
Figure 6-4. Preview of drag-and-drop slideshow editing
Let’s start by updating the slideshow’s edit template. Edit photos/app/views/slideshows/edit.rhtml to look like this:
<h1>Editing slideshow</h1>
<div id=’slideshow-contents’>
<p style=’text-align: center;’><b>Slideshow Photos</b></p>
<div id=’slideshow-thumbs’>
<%= render :partial => ’show_slides_draggable’ %>
</div>
</div>
<div id=’slideshow-photo-picker’>
<p style=’text-align: center;’><b>Unused Photos</b></p>
<div id=’slideshow-photos’>
<%= render :partial => ‘photo_picker’ %>
</div>
</div>
<div id=’slideshow-attributes’>
<p><%= link_to ‘Play this Slideshow’, :action => ’show’, :id => @slideshow %></p>
<div style=’border: thin solid; padding-left: 1em;’>
<p style=’text-align: center;’><b>Attributes</b></p>
<%= start_form_tag :action => ‘update’, :id => @slideshow %>
<%= render :partial => ‘form’ %>
<%= submit_tag ‘Save Attributes’ %>
<%= end_form_tag %>
</div>
<p>
<b>Hint:</b> Drag and drop photos between the
two lists to add and remove photos from the
slideshow. Drag photos within the slideshow to
rearrange their order.
</p>
</div>
<%= drop_receiving_element(”slideshow-contents”,
:update => “slideshow-thumbs”,
:url => {:action => “add_photo” },
:accept => “photos”,
:droponempty => “true”,
:loading => visual_effect(:fade),
:complete => visual_effect(:highlight, ’sortable_thumbs’)
) %>
This file has been almost entirely rewritten, so there are no marked-as-changed lines. You can see that I have laid out this edit page into three sections:
<div id=’slideshow-contents’> . . . </div>
<div id=’slideshow-photo-picker’> . . . </div>
<div id=’slideshow-attributes’> . . . </div>
Only the slideshow-photo-picker is new. It shows the list of unused photos that can be added to the slideshow. We will set up the CSS stylesheet to display these sections side-by-side as you saw them in Figure 6-4.
slideshow-contents is rendered by the partial template show_slides_draggable,
slideshow-photo-picker is rendered by the partial template photo_picker, and slideshow-attributes is mostly rendered by the form partial template that was generated from the scaffolding. I say “mostly” because I added a few things inline around the rendering of form.
Finally, notice two Ajax related helpers: drop_receiving_element and observe_field. We’ll come back to these in a little bit after we have discussed some prerequisite details.
Now, make these changes to photos/app/controllers/slideshows_controller.rb, replacing the edit method and creating the unused_photos method:
def edit
@slideshow = Slideshow.find(params[:id])
session[:slideshow] = @slideshow
@photos = unused_photos(@slideshow)
end
def unused_photos(slideshow)
all_photos = Photo.find(:all)
candidates = []
for photo in all_photos
in_slideshow = false
for slide in slideshow.slides
if slide.photo.thumbnail === photo.thumbnail
in_slideshow = true
break
end
end
candidates << photo if not in_slideshow
end
return candidates
end
The purpose of this code is to retrieve all the data needed by the edit.rhtml view template:
@slideshow = Slideshow.find(params[:id])
The id of the slideshow that you want to edit is passed in the request parameters from the browser. Here you retrieve that id and read that slideshow from the database, which you store in the instance variable @slideshow to make it available to the view template.
session[:slideshow] = @slideshow
Ajax actions requests will be coming in as the user makes changes, and you need to know what slideshow to change. This line saves a reference to the slideshow in the session hash. I’m using a key value of :slideshow to save and retrieve this from the session, but that value is arbitrary and could have been any unique identifier.
@photos = unused_photos(@slideshow)
This line calls the new method unused_photos to retrieve a list of all photos that are not in the slideshow; it then saves that list in @photos.
def unused_photos(slideshow)
This method returns a list of photos that are not in the slideshow. The logic should be self-explanatory. First, create an empty array (candidates = []), and then iterate through the list of all photos, adding them to the array (candidates << photo) if they are not already in the slideshow. The technique used here is grossly inefficient, but it will suffice for our purposes.
We still need to create the photo_picker template that generates the HTML to display all the photos that can still be added to a slideshow, so go ahead and create the file photos/app/views/slideshows/_photo_picker.rhtml with this in it:
<% for photo in @photos %>
<%= image_tag(”photos/#{photo.thumbnail}”,
:style => “vertical-align: middle”,
:id => “photo_#{photo.id}”,
:class => “photos”) %>
<%= draggable_element “photo_#{photo.id}”, :revert => true %>
<% end %>
This template iterates through the list of photos in @photos. For each photo, it uses the image_tag helper to create an HTML image tag and the draggable_element helper to generate the JavaScript code that makes it draggable. You can see that the first parameter of draggable_element matches the value of the id attribute (:id => “photo_#{photo.id}”) on the image tag. The draggable_element helper expects the id of the HTML element that it should make draggable, followed by zero or more options. The single option used here (:revert => true) says to move the element back to its original position after it is dropped.
But where can these draggable images be dropped? Recall that at the end of the slideshow’s edit.rthtml template we had:
<%= drop_receiving_element(”slideshow-contents”,
:update => “slideshow-thumbs”,
:url => {:action => “add_photo” },
:accept => “photos”,
:droponempty => “true”,
:loading => visual_effect(:fade),
:complete => visual_effect(:highlight, ’sortable_thumbs’)
) %>
Just like the draggable_element helper, the drop_receiving_element helper expects the ID of the HTML element onto which you can drop something that was declared as draggable. The remaining parameters are options that given as name/value pairs (the order is not important). These options are doing a lot, so let’s go through them one at a time:
:update => “slideshow-thumbs”
This gives the ID of the HTML element that should be updated when a photo is dropped on our slideshow-contents div. The :position and :url options say how, and with what, that HTML element should be updated. When the :position option is omitted (as it is here), the HTML returned from the server replaces the target elements HTML. The :position option says that the returned HTML should be inserted into target element, instead of replacing it. The value :position can be specified as :before, :top, :bottom, and :after.
:url => {:action => “add_photo” }
This option constructs the URL that is sent to the server (via a background Ajax request) when a photo is dropped (you’ve seen this before). This executes the add_photo method in the current controller (the SlideshowsController). The add_ photo action adds the dropped photo to the slideshow and returns an HTML fragment that will replace the existing HTML in the target element, which, as you will see, is a rerendering of the slideshow’s contents, which now include the added photo.
:accept => “photos”
Without this option, you could drop any draggable element here. However, this line says that only HTML elements that have the class attribute “photos” can be dropped here. Remember that in our photo picker template we gave each photo class attribute of “photos”.
:droponempty => “true”
This option says that the user can drop photos here even if the target is completely empty.
:loading => visual_effect(:fade)
:complete => visual_effect(:highlight, ’sortable_thumbs’)
:loading and :complete (plus a few more events) specify client-side JavaScript event handlers that are executed at specific points in the progress of the Ajax request. In both cases, we are displaying a visual effect that gives the user positive feedback. The :loading event occurs when the browser begins loading the response, and the :complete event occurs when its all finished. The code specifies that the dropped photo will fade until it becomes invisible. It also highlights the target area on which the photo was dropped.
Adding a Dropped Photo
Now we need to create the add_photo method to actually add a dropped photo to the slideshow. Edit photos/app/controllers/slideshows_controller.rb, and add this:
def add_photo
slideshow_id = session[:slideshow].id
photo_id = params[:id].split(”_”)[1]
slide = Slide.new()
slide.photo_id = photo_id
slide.slideshow_id = slideshow_id
if !slide.save
flash[:notice] = ‘Error: unable to add photo.’
end
@slideshow = Slideshow.find(slideshow_id)
session[:slideshow] = @slideshow
render_partial ’show_slides_draggable’
end
Let’s walk through this code:
slideshow_id = session[:slideshow].id
This line retrieves the current slideshow from the session hash and gets the slideshow’s id.
photo_id = params[:id].split(”_”)[1]
The id attribute of the dropped photo get passed as the :id parameter. If you recall from the photo_picker template, we set those ids to values such as “photo_1″ and “photo_19″, so the remainder of this line of code splits the string on the underscore, grabs the second half, and assigns it to photo_id.
The next five lines create a new slide, assign to it the photo id and the slideshow id, and then save it to the database.
Finally, we render and return the show_slides_draggable partial, after setting @slideshow to the current slideshow (which is needed by the partial template).
All that code handles dragging new photos to add to the slideshow. Now we just need to add a little more code to implement dragging a photo from the slideshow to the unused photos list as an intuitive way to remove photos from the slideshow.
The displayed list of photos in the slideshow are already draggable because we made them into a sortable list. The only problem with the current implementation is that the photos can be dragged vertically only. They need to be dragged both vertically for reordering and horizontally to the unused photos column.
We can drag the photos only vertically because the default option for a sortable list is :constraint => ‘vertical’. Fortunately, you can change this by editing the file photos/app/views/slideshows/_show_slides_draggable.rhtml and changing the call to the sortable_element helper to add this :constraint option:
<%= sortable_element(’sortable_thumbs’,
:url => {:action => ‘update_slide_order’},
:constraint => ”) %>
Now you can drag those photos anywhere. But you still need to make the unused photos list into a drop receiver that uses Ajax to remove the dropped photo from the slideshow.
To do so, edit photos/app/views/slideshows/edit.rhtml, and add this at the end:
<%= drop_receiving_element(”slideshow-photo-picker”,
:update => “slideshow-photos”,
:url => {:action => “remove_slide” },
:accept => “slides”,
:droponempty => “true”,
:loading => visual_effect(:fade),
:complete => visual_effect(:highlight, ’slideshow-photos’)
) %>
This code is almost identical to the other drop_receiving_element we used. The difference is that the target is the slideshow-photo-picker, and the action taken on a drop is to call the remove_slide method. Also, notice that you can drop only “slides” here (that is, HTML elements with a class attribute of slides). If you go back and take a look at how we defined the partial template photos/app/views/ slideshows/_show_slides_draggable.rhtml, you will see that we did, indeed, make each item in the sortable list a slide.
Add the remove_slide method to photos/app/controllers/slideshows_controller.rb:
def remove_slide
slideshow_id = session[:slideshow].id
slide_id = params[:id].split(”_”)[1]
Slide.delete(slide_id)
@slideshow = Slideshow.find(slideshow_id)
session[:slideshow] = @slideshow
@photos = unused_photos(@slideshow)
render_partial ‘photo_picker’
end
In this code, you get the id of slide you want to remove, and then delete it from the slide database table. Remember, this action does not delete the photo from the database. The slide data says what photos are in a given slideshow, and deleting an entry from the slide table removes that slide from its slideshow. Finally, you render the HTML for the photo picker, which now includes the removed slide.
I’ll bet you’re anxious to see all this in action. All you need to do is to update the style sheet and then try it out. Edit photos/public/stylesheets/slideshows.css, and add the following:
#slideshow-photo-picker {
float: left;
width: 10em;
text-align: center;
border-right: thin solid #bbb;
padding: 0.50em;
padding-bottom: 10em;
}
img.thumbnail {
border: 2px solid black;
margin-bottom: 1em;
}
img.photos {
border: 2px solid black;
margin-bottom: 1em;
}
Whew! That’s it: try it now!
The first thing you’ll notice is that the Unused Photos section is empty (see Figure 6-5). That’s because all the photos are currently in the slideshow. Just drag a few of the slides out of the slideshow and drop them into the Unused Photos column; then you’ll have something more like Figure 6-6.
Filtering by Category
Displaying all unused photos might seem acceptable right now, but we have only nine photos. If there were 900, it would quickly become unusable. So, our final feature in this chapter will be to display only the unused photos in a particular category.
The first thing to do in our controller is get a list of all categories that can populate the drop-down selection box. Edit photos/app/controllers/slideshows_controller.rb, and add this line to the end of the edit method:
@all_categories = Category.find(:all, :order=>”name”)
This line retrieves a list of categories that can populate a drop-down selection box that the user will use to display only those unused photos that are in the selected category.
Figure 6-5. Drag and drop add and remov
Now, edit photos/app/views/slideshows/edit.rhtml, and add this right after the ‘Play this Slideshow’ line:
<p>
<label for=”category_id”>Filter “Unused Photos” on this Category</label><br/>
<%= collection_select(:category, :id, @all_categories, :id, :long_name) %>
<%= observe_field(:category_id,
:frequency => 2.0,
:update => ’slideshow-photos’,
:url => { :action => ‘change_filter’},
:with => ‘category_id’ ) %>
</p>
The collection_select helper is normally used inside an HTML form, but here we are using it because it conveniently knows how to display a collection in a drop-down box. It will never be submitted as part of a form.
As shown, the observe_field helper checks the category drop-down box for changes every two seconds. When a change is detected, an Ajax request is fired off to the change_filter method, which returns new HTML (that has been appropriately
filtered) to replace the slideshow-photos section.
Figure 6-6. Some unused photos
The Category model class automatically shows a collection of all photos that are in a particular category. However, we need to get a collection of photos that are in a given category and in all of its child categories.
Edit photos/app/models/category.rb, and add this method:
def photos_including_child_categories
result = photos.clone
children.each do |c|
c.photos_including_child_categories.each {|p|
result << p if not result.include? p}
end
result
end
This method recursively collects a list of all photos in its own category and all of its child categories. You can use this in to get the list of unused photos to display.
In the meantime, edit photos/app/controllers/slideshows_controller.rb to add the change_filter method:
def change_filter
slideshow_id = session[:slideshow].id
category_id = params[:category_id] || 1
session[:category_id] = category_id
@slideshow = Slideshow.find(slideshow_id)
session[:slideshow] = @slideshow
@photos = unused_photos(@slideshow)
render_partial ‘photo_picker’
end
This method stores the chosen category id in the session hash, retrieves a new list of unused photos, and then renders the photo_picker. Notice the bold code line in the previous code. This line tries to retrieve the category id from the request parameters. If there aren’t any parameters, params[:category_id] returns nil, and the || operator returns the rightmost argument (”1″ in this case).
Also, in this slideshow controller, we need to update the method that retrieves the unused photos to pay attention to the category setting. Do so by editing the unused_ photos method; then replace the line all_photos=Photo.find(:all) with the following:
category_id = session[:category_id] || 1
session[:category_id] = category_id
category = Category.find(category_id)
all_photos = category.photos_including_child_categories
We’re done; we’ve added category filtering! Fire up your browser, and try it (you may need to assign some categories to some unused photos). Now it looks like Figure 6-7.
Figure 6-7. Filtering on categories
We’ve come a long way in a very short time. With fewer than 200 lines of code, we’ve added drag-and-drop capability to add and reorder slides. We’ve also added the core capability to actually show a slideshow. Ajax made our application much easier to use and more attractive. Next, we’ll look into testing this application.
—
by O’Reilly Media
20 Oct
Posted by ProCOM
on October 20, 2007 – 1:23 pm - 1,947 views
In this first article in a two-part series, you will learn how Ruby on Rails implements Ajax. We will use the specific example of a slideshow to demonstrate our points. This article is excerpted from chapter six of the book Ruby on Rails: Up and Running, written by Bruce A. Tate and Curt Hibbs (O’Reilly, 2006; ISBN: 0596101325). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
Ajax is one of the most important emerging trends in web applications. Web sites like Google Maps and Gmail dramatically demonstrate that web applications do not have to be slow, clunky, page-at-a-time web forms. Ajax techniques can reclaim some of the fluidity and responsiveness that was lost when we moved from desktop applications to web applications.
Ajax (which stands for the cryptic “Asynchronous JavaScript and XML”) is a technique for building web pages that are more interactive, exciting, and dynamic. Ajax is asynchronous: JavaScript libraries can communicate with the server at any time, and the web page need not be frozen while waiting for a response. Ajax uses JavaScript on the browser, any language on the server, and XML to specify messages.
When you use this emerging technique, a web page can communicate with the server at any time, updating only those portions of the display that need it. Users experience more responsive web pages, with immediate feedback. Even though using Ajax techniques usually requires significantly more sophisticated design and implementation skills, the benefits to the end user are so great that Ajax-enabled web applications will soon become the rule, not the exception. Fortunately, Rails makes Ajax so simple that, for typical cases, using Ajax is almost as easy as not using it.
How Rails Implements Ajax
Rails has a simple, consistent model for how it implements Ajax operations. Once the browser has rendered and displayed the initial web page, different user actions cause it to display a new web page (like any traditional web application) or trigger an Ajax operation:
Some trigger fires
This trigger could be the user clicking on a button or link, the user making changes to the data on a form or in a field, or just a periodic trigger (based on a timer).
The web client calls the server
A JavaScript method, XMLHttpRequest, sends data associated with the trigger to an action handler on the server. The data might be the ID of a checkbox, the text in an entry field, or a whole form.
The server does something
The server-side action handler–a Rails controller action (for our purposes)–does something with the data and returns an HTML fragment to the web client.
The client receives the response
The client-side JavaScript, which Rails creates automatically, receives the HTML fragment and uses it to update a specified part of the current pages HTML, often the content of a <div> tag.
These steps are the simplest way to use Ajax in a Rails application, but with a little extra work, you can have the server return any kind of data in response to an Ajax request, and you can create custom JavaScript in the browser to perform more involved interactions. We’ll stick to HTML fragments in this chapter.
Rails uses the Prototype and script.aculo.us JavaScript libraries to implement browser support for Ajax. You can use these libraries independently of Rails, but with their seamless integration with Rails, you probably wont want to. Throughout this chapter, we’ll exploit the Ajax and special-effects capabilities that come with Rails to implement missing features in our Photo Share application.
Playing a Slideshow
Let’s see what happens when we try to play a slideshow. Browse to http://127.0.0.1: 3000/slideshows/list, and click the Play link for our only slideshow. As you can see in Figure 6-1, this URL invokes the show action on the slideshow controller, but the action is still using the scaffold code.
We need to change this page to actually “play” the slideshow by sequentially displaying the pictures contained in the slideshow. To do this, we will initially display the first picture in the slideshow; then, once every two seconds, we’ll make an Ajax call to get and display the next picture.
The controller sets up all the slides in a slideshow for playback. You need to start @slideshow with the current slideshow set to play. You also need to put the current slide (initially, 0) and the whole slideshow into a holding area called the session, so you won’t have to read from the database each time you play a new slide. Edit the slideshows controller (app/controllers/slideshows_controller.rb), and modify the show method to look like this:
def show
@slideshow = Slideshow.find(params[:id])
session[:slideshow] = @slideshow
session[:slide_index] = 0
Figure 6-1. Playing a slideshow that still uses scaffolding
@slide = @slideshow.slides[0]
end
Every two seconds in this code, the browser sends an Ajax request to get the next slide. You can’t use instance variables to keep track of where you are in the slideshow because instance variables exist only until you finish processing the current request. Use the Rails-provided session object instead, which is persistent across requests. Let’s look at this code in a little more detail.
session[:slideshow] = @slideshow stores a reference to the current slideshow in the session hash at the key :slideshow. We do the same thing with the index of the current slide that is being played. We initially set the slide_index to zero to point to the first slide, and our Ajax request increments the index by one as it displays each slide. We can retrieve these values from the session hash during the Ajax requests for the next slide.
Now, edit the view template (app/views/slideshows/show.rhtml) to look like this:
<p><i><%= @slideshow.name %></i></p>
<div id=”slides”>
<%= render :partial => “show_slide” %>
</div>
<%= periodically_call_remote :update => ’slides’,
:url => { :action => :show_slide },
:frequency => 2.0 %>
This RHTML template contains three things: a title line, the div that displays the current slide, and a magic Ajax incantation that we will now pick apart.
The periodically_call_remote Rails helper function creates JavaScript that periodically sends a request to the server and uses the HTML fragment that is returned to replace the content of the update target. In this case, the update target is an HTML element with an ID of ’slides’, which is a <div> tag. The returned HTML fragment replaces the contents of this <div> tag. The URL that makes the request is constructed to ensure that it will be routed to the show_slide method of the current controller (the slideshows controller). Finally, the frequency parameter makes the call once every two seconds. All Ajax help functions take their parameters in key/value pairs, so you can list the parameters in any order.
We need to display each slide as it comes back to the client; Rails uses a partial HTML template to do this work. To create the partial view template, place the following contents in a new file called app/views/slideshows/_show_slide.rhtml:
<%= image_tag “photos/#{@slide.photo.filename}” %>
<p><%= @slide.photo.filename %></p>
And its controller method in app/controllers/slideshows_controller.rb:
def show_slide
@slideshow = session[:slideshow]
session[:slide_index] += 1
@slide = @slideshow.slides[session[:slide_index]]
if @slide == nil
session[:slide_index] = 0
@slide = @slideshow.slides[0]
end
render :partial => “show_slide”
end
This method retrieves the slideshow information from the session, moves to the next slide (or back to the beginning if at the end), and then explicitly renders the partial view template show_slide. You need to render a partial view or render with the option :render_layout => false . Otherwise, Rails tries to render a full template, including layout. As our page already has a layout, simply render a partial template, consisting of an image tag for the slide, and its name.
Finally, you need to update your standard layout template to include script tags for the Prototype JavaScript library because the client-side JavaScript code that Rails creates for you uses them, so in app/views/layouts/standard.rhtml, insert this line immediately after the title tags:
<%= javascript_include_tag ‘prototype’, ‘effects’, ‘dragdrop’ %>
This line includes three JavaScript files that are shipped with Rails: prototype.js and two Script.aculos.us files, effects.js and dragdrop.js. We will use these last two shortly.
Now show a slideshow by loading slideshows/list and clicking Show, and you will see the actual pictures in the slideshow, changing every two seconds.
Using Drag-and-Drop to Reorder Slides
The scaffolding we have for editing a slideshow shows just the slideshow attributes that are stored directly in the slideshows table: the slideshows name and the date on which it was created. The most important part is missing: the photos that are part of the slideshow!
By now, you’ve probably realized that this is because the scaffolding code deals with only one database table: the slideshows table. The relationship data about which photos are assigned to a slideshow and their order in the slideshow are stored in the slides table. Scaffolding does not handle relationships, so you have to write the code to edit this relationship data.
We’re going to display a list of thumbnails of all the photos that are in a slideshow, and then let the user reorder them using drag-and-drop. If you’ve had to struggle through implementing drag-and-drop before, you’re not going to believe how easy this is going to be. Here’s a hint: this will take a total 34 additional lines of Ruby, CSS, and RHTML template!
Let’s start by reviewing the current implementation of the edit action in the slideshow controller:
def edit
@slideshow = Slideshow.find(params[:id])
end
This action expects to find the ID of the slideshow to edit passed in as the id parameter, which is normally decoded from the URL. You find the slideshow with that ID and assign that slideshow object to the instance variable @slideshow, so that it can be accessed in the view template.
That is really all that’s needed here, so you won’t have to add any code to this method. The changes will start with the edit view template, so edit the template photos/app/views/slideshows/edit.rhtml and make it look like this (the changes are in bold):
<h1>Editing slideshow</h1>
<%= link_to ‘Play this Slideshow’,
:action => ’show’, :id => @slideshow %>
<div id=’slideshow-contents’>
<%= render :partial => ’show_slides_draggable’ %>
</div>
<div id=’slideshow-attributes’>
<%= start_form_tag :action => ‘update’, :id => @slideshow %>
<%= render :partial => ‘form’ %>
<%= submit_tag ‘Save Attributes’ %>
<%= end_form_tag %>
</div>
Notice that the existing <%= render :partial => ‘form’ %> is wrapped in a <div> tag with an id attribute of slideshow-attributes. You will use this name in one of your CSS files to control how this section is displayed.
There is also a completely new section that displays thumbnails of the photos in the slideshow:
<div id=’slideshow-contents’>
<%= render :partial => ’show_slides_draggable’ %>
</div>
This code also uses a <div> tag with an id attribute, for the same reason: to use a CSS file to control its appearance. This div also renders a new partial view template named show_slides_draggable, which we will create next.
Create the file photos/app/views/slideshows/_show_slides_draggable.rhtml with the following contents:
<ol id=’sortable_thumbs’>
<% for slide in @slideshow.slides %>
<li id=’thumbs_<%= slide.id %>’ class=’slides’>
<%= thumbnail_tag slide %>
</li>
<% end %>
</ol>
<%= sortable_element(’sortable_thumbs’,
:url => {:action => ‘update_slide_order’}) %>
The first part is pretty standard stuff. We’re creating an HTML ordered list, in which each list item is a thumbnail image of one of the photos in the slideshow (note that the thumbnail_tag helper function that was created earlier). However, it’s the last two lines that do the heavy lifting.
sortable_element is a helper function that generates the JavaScript code that turns our list into a user-sortable, drag-and-drop-capable list. It wraps this list an HTML form, and the :url option specifies the URL to post to the server whenever the user changes the order of the list. In this case, it calls the action method update_slide_order in our slideshow controller. This call works in the background using an Ajax call.
The update_slide_order method is pretty simple as well. Edit photos/app/controllers/ slideshows_controller.rb, and add this method:
def update_slide_order
params[:sortable_thumbs].each_with_index do |id, position|
Slide.update(id, :position => position)
end
end
This method iterates through each slide in the list, extracting its ID and position in the list, and uses this information to update that slide’s database row with its new position. Let’s walk through this code in a little more detail:
We’re almost ready to give it a try, but first let’s edit photos/public/stylesheets/slideshows.css and add some formatting instructions for the two div IDs we created. Add the following at the end of the file:
#slideshow-contents {
float: left;
width: 11em;
padding: 0.50em;
text-align: center;
border-right: thin solid #bbb;
padding: 0.50em;
padding-bottom: 10em;
}
#slideshow-attributes {
margin-left: 23em;
padding-left: 1.5em;
padding-top: 1.5em;
}
This causes the contents of the slideshow (which will be a list of thumbnail images) to be displayed down the left side of the page, and the slideshow’s attributes will be displayed immediately to the right of the thumbnails.
Let’s see how this looks. Browse to http://127.0.0.1:3000/slideshows/list, and click the edit link for our one and only slideshow. It will look like Figure 6-2.
Click on one of the photos, and try dragging it around. When you drop it into a new location, update_slide_order is called to write the new order to the database.
Lets fix one minor thing here before we move on. Wouldn’t it be better to see the number of each photo appear vertically aligned in the middle of the thumbnail instead of at the bottom? Because the HTML for each thumbnail image is created by…
Figure 6-2. A drag-and-drop list of photos
…
our own helper function, thumbnail_tag, we just need to edit that function and add a vertical-align style attribute.
First, edit photos/app/helpers/slideshows_helper.rb, and add the code shown in bold:
module SlideshowsHelper
def thumbnail_tag(slide)
image_tag(”photos/#{slide.photo.thumbnail}”,
:style=>”vertical-align:middle”) if slide
end
end
Now, refresh your browser: the list numbers are nicely centered, as you can see in Figure 6-3.
With a very small amount of code, we added a very nice drag-and-drop user interface for reordering the slides in a slideshow. But we’re just getting started with our Ajax-enabled user interface.
Figure 6-3. Nicely centered list numbers
Please check back tomorrow for the conclusion to this article.
—
by O’Reilly Media
Java and Flash offer real alternatives for those trying to build web applications that function more like desktop applications. However, they have their shortcomings. AJAX overcomes these shortcomings — especially if you use Ruby on Rails.
Interactivity and responsiveness has been considered the USP of desktop applications and not of web applications. Even though there exist mechanisms such as Java Applets and Flash files that provide interactivity and responsiveness up to a point, they are intrusive. That is, the user needs to install either a Java or a Flash plug-in.
However, with AJAX coming into the foreground, a non-intrusive way of providing interactive and responsive web applications has evolved. Even with AJAX, the problem is not fully solved for developers. The reason for this is the peripheral integration of AJAX in almost all the existing frameworks. Still, there are exceptions in the form of frameworks that provide AJAX-based functionalities as their core services. Ruby-on-Rails (RoR) is one of them.
In this discussion, I will be focusing on using AJAX with RoR. The first section will be a brief overview of AJAX. In the second section, I will detail the steps involved in AJAXifying the RoR application. In the last section, I will be developing a real world application using AJAX and RoR. That’s the agenda for this discussion.
AJAX: an Overview
By definition AJAX is “a technique that extends the traditional web application model to allow for in-page server requests.” In other words, AJAX provides the functionality to make on the fly requests to the server and present the response data to the user by modifying a part of the page without refreshing the whole page. AJAX is not a single technology; rather, it’s a combination of technologies. These technologies form the basic components of AJAX, which are:
The second component is an object of JavaScript itself. However the first ‘A’ of AJAX comes from it. The reason for this is detailed below.
JavaScript is the “J” in AJAX. It is the JavaScript that parses the response from the server and makes appropriate changes to the current page. Using JavaScript, one can change a paragraph, replace a product image or change the complete look and feel of the page without the intervention of the server. This reduces the bandwidth and makes the web site look more responsive to the user.
XMLHttpRequest is an object of JavaScript that not only allows the user to create or construct HTTP calls from within the script but also process the response sent by the server. It is available as an ActiveX control in Internet Explorer and as a JavaScript object in other browsers. Though it is known as XMLHttpRequest, one can wrap any kind of HTTP communication using it.
The first ‘A’ in AJAX, which stands for “Asynchronous,” comes at the “behest” of XMLHttpRequest. One of the abilities of XMLHttpRequest is to make a call to the web server in the background while the user continues to do his/her work on the UI. Whenever the response is received, the XMLHttpRequest accepts it and passes it over to the function that is registered to handle the response. Thus the request-response cycle is relieved off the paradigm of synchronous communication.
If one looks up the definition of XML on the net, the most common definition found is this: “XML (Extensible Markup Language) is a W3C initiative that allows information and services to be encoded with meaningful structure and semantics that computers and humans can understand and is great for information exchange, and can easily be extended to include user-specified and industry-specified tags.” The extensibility and the ability to provide data in a structured format is the main reason for XML to be the preferred response data format sent by the server. This doesn’t mean that the server can’t send data in any other format. It can send data in any format and XMLHttpRequest can accept and process it. However, since XML is a standard that is both extensible and structured, it is preferred over every other.
The next obvious question is how these three components fit in the overall picture. Here is an example of AJAX at work. It checks the validity of the username.
var xmlHttp;
function createXmlHttpRequest()
{
//check the type of browser and initialize XMLHttpRequest object
if(window.ActiveXObject)
{
xmlHttp=new ActiveXObject(”Microsoft.XMLHTTP”);
}
else if(window.XMLHttpRequest)
{
xmlHttp=new XMLHttpRequest();
//alert(”inside elseif…”+xmlHttp);
}
}
//This method creates an instance of XMLHttpRequest and calls the server
//Then delegates the data processing to another function on receipt of data
function startRequest()
{
createXmlHttpRequest();
var u1=document.f1.user.value;
xmlHttp.open(”GET”,”validate.php?user=”+u1,true)
xmlHttp.onreadystatechange=handleStateChange;
xmlHttp.send(null);
}
//Does the handling of data and presentation of data
function handleStateChange()
{
if(xmlHttp.readyState==4)
{
if(xmlHttp.status==200)
{
document.getElementById(”results”).innerHTML= xmlHttp.responseText;
}
else
{
alert(”Error loading pagen”+ xmlHttp.status +”:”+ xmlHttp.statusText);
}
}
}
function resetDisplay()
{
document.getElementById(”results”).innerHTML=” ”
}
The logic here is very simple, yet the amount of boilerplate code required is pretty high and the complexity of JavaScript increases with the increase in complexity of the logic of data processing and presentation. And I have not shown the logic at server-side. This is the story with almost all of the technology except Ruby-on-Rails. How RoR is different from the others is the topic of the next section.
Implementing AJAX
In the last section, the example demonstrated the complexity involved in AJAXifying an application having even the simplest logic to be implemented. So how can RoR make it simple? RoR does it by providing AJAX as one of its core functionalities. RoR has the prototype, effects, dragdrop and controls JavaScript libraries as built-in libraries. Secondly, a helper called JavaScriptHelper provides wrapping around JavaScript code so that switching between Ruby and JavaScript won’t be necessary. So RoR makes things simpler by wrapping JavaScript libraries and providing them as built-in RoR libraries.
Now let’s look at ways to use AJAX within RoR. To implement AJAX, RoR provides two basic ways which are:
The former is used with non-form HTML whereas the later is used with form and form based HTML elements.
The link_to_remote() is one of the simplest yet versatile and flexible helpers in RoR. It takes following parameters:
So what are the steps required to use link_to_remote()? The steps are as follows:
The steps are common to that of any typical RoR. That is the beauty of RoR. Of these steps, the third step is optional if rendering is taken care of by the action itself.
<%= javascript_include_tag “prototype” %>
Here is an example of a page having link_to_remote():
<html>
<head>
<title>Ajax Demo</title>
<%= javascript_include_tag “prototype” %>
</head>
<body>
<h1>What time is it?</h1>
<div id=”time_div”>
I don’t have the time, but
<%= link_to_remote( “click here”,
:update => “time_div”,
:url =>{ :action => :say_when }) %>
and I will look it up.
</div>
</body>
</html>
The link_to_remote() is passed three arguments: the text to be shown i.e. “click here”, the id of the link created which is “time_div” and the action to be called which is say_when. The next step is to implement the controller containing the action.
class DemoController < ApplicationController
def index
end
def say_when
render(:layout => false)
end
end
The render function is called with the layout option as false because only a part of the HTML page is being updated; hence there is no requirement of any Rails layout wrappers. The example of the second type is as follows:
class DemoController < ApplicationController
def index
end
def say_when
render_text “<p>The time is <b>” + DateTime.now.to_s + “</b></p>”
end
end
It uses render_text method to generate the response.
<p>The time is <b> <%=DateTime.now.to_s%>
</b></p>
That’s how link_to_remote() is used. Next let’s see how form_remote_tag() is used.
While dealing with links, link_to_remote() is handy but what about form elements? That’s where form_remote_tag() comes in. One can easily enable any Rails form to use AJAX using form_remote_tag(). It serializes and sends all the form elements to the server via XMLHttpRequest. All this is done automatically without any requirement for extra logic implementation. The form_remote_tag() takes three parameters:
To use the form_remote_tag() there are three steps:
The steps are similar to those of using link_to_remote(). Only the way they are implemented is different.
<html>
<head>
<title>Ajax List Demo</title>
<%= javascript_include_tag “prototype” %>
</head>
<body>
<h3>Add to list using Ajax</h3>
<%= form_remote_tag(:update => “my_list”,
:url => { :action => :add_item },
:position => “top” ) %>
New item text:
<%= text_field_tag :newitem %>
<%= submit_tag “Add item with Ajax” %>
<%= end_form_tag %>
<ul id=”my_list”>
<li>Original item… please add more!</li>
</ul>
</body>
</html>
Here the id of the element is given as my_list which is the id of the list that will be populated by the response provided by the action. The url specifies the action to be called and the position tells Rails to place the returned HTML snippet on the top of the element whose id has been specified in the update parameter. Now let’s look at the action.
class ListdemoController < ApplicationController
def index
end
def add_item
render_text “<li>” + params[:newitem] + “</li>”
end
end
whereas if the rendering is to be done by a template then the code would be:
class ListdemoController < ApplicationController
def index
end
def add_item
@item= params[:newitem]
render(:layout => false)
end
end
The only difference is that the parameter passed by form_remote_tag() is placed in a variable called item which can be used in the template.
<li> <%=@item%> </li>
That’s it.
So now you have seen how easy the steps are for creating an AJAX based application in RoR. Next I will develop a real world application using RoR and AJAX.
Ruby-on-Rails and AJAX
In real world applications one of the most required functionalities is to populate a combo box based on the data entered in a preceding field such as a text box or another combo box. In this example, I will be developing such a part for an application. This can be used with any application with slight modifications. It contains two components:
index.rhtml – the view which contains the combo box to be filled and the
combo box on the basis of which the filling has to be donePopulateController – the controller containing the action required for the
populating of the combo box.
Here is the index.rhtml:
<html>
<head>
<meta http-equiv=”content-type” content=”text/html; charset=utf-8″ />
<%= javascript_include_tag “prototype” %>
<title>Ajax Rails & Select lists</title>
</head>
<body>
<%= form_remote_tag(:update => “sel_con”,:url => {
:action => :create_select },
:position => “top”,:success => “$(’sel_con’).innerHTML=”” ) %>
<p>
Please select a sports category:
</p>
<p>
<%= select_tag “categories”,
“<option>Team</option><option>Individual</option>” %>
</p>
<div id=”sel_con”></div>
<p>
<%= submit_tag “Show Sports” %>
</p>
<%= end_form_tag %>
</body>
</html>
The combo box contains two values, team and individual. Based on the selection the second combo has to be populated and shown at the div having the id sel_con. If team is selected by the user, the second combo box would be filled with team events whereas in the case of individual, individual event is populated. The form_remote_tag is passed four parameters: the id of element to be updated, the action to be called, the position in which to place the result and what has to be done if the request is successfully executed. In this case the inner HTML is set to no value i.e. “”. Next is the controller:
class PopulateController < ApplicationController
def index
end
def create_select
indArr=["Nordic Skiing", "Inline Skating","Tennis",
"Triathlon","Road Racing","Figure Skating",
"Weight Lifting","Speed Skating","Snowboarding"];
teamArr=["Soccer","Basketball","Football","Hockey",
"Baseball","Lacrosse"];
str=”";
if params[:categories].index(’Team’) != nil
render :partial => “options”,
:locals => { :sports => teamArr,:sptype => “team”}
elsif params[:categories].index(’Individual’) != nil
render :partial => “options”,
:locals => { :sports => indArr, :sptype => “individual” }
else
str=”<select id=’individual’ name=’individual’>
<option>unknown</option></select>”;
render :text => str;
end
#end method
end
#end class definition
end
The create_select is the action where the logic for creating and populating the second combo box goes. First it creates two arrays with team and individual sports events. Then based on the parameter received, the combo box is created and populated. This is done in the statement
render :partial => “options”,
:locals => { :sports => teamArr,:sptype => “team”}
Now let’s look at _options.rhtml which is a partial template i.e. the a piece of HTML code that can be used again and again. Here is the code:
<select id=”<%= sptype %>” name=”<%= sptype %>”>
<% sports.each do |sport| %>
<option><%= sport %></option>
<% end %>
</select>
It first gives the combo box an id, iterates through the array passed and populates the combo box. That’s it. This completes the application. Though I had mentioned four libraries, I have introduced only one of them. In the next discussion, I will be focusing on UI using the other three libraries. Till then..
—
by A.P.Rajshekhar
In this fifth article of a six-part series covering web development and Ruby on Rails, you’ll learn how to create an AJAX form and more. This article is excerpted from chapter 15 of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O’Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.
15.16 Generating Forms for Manipulating Model Objects
Problem
You want to define actions that let a user create or edit objects stored in the database.
Solution
Let’s create a simple model, and then build forms for it. Here’s some MySQL code to create a table of key-value pairs:
use mywebapp_development;
DROP TABLE IF EXISTS items;
CREATE TABLE `items` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL default ”,
`value` varchar(40) NOT NULL default ‘[empty]‘,
PRIMARY KEY (`id`)
);
Now, from the command line, create the model class, along with a controller and views:
$ ./script/generate model Item
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/item.rb
create test/unit/item_test.rb
create test/fixtures/items.yml
create db/migrate
create db/migrate/ 001_create_items.rb
$ ./script/generate controller items new create edit
exists app/controllers/
exists app/helpers/
create app/views/items
exists test/functional/
create app/controllers/ items_controller.rb
create test/functional/ items_controller_test.rb
create app/helpers/items_helper.rb
create app/views/items/new.rhtml
create app/views/items/edit.rhtml
The first step is to customize a view. Let’s start with app/views/items/new.rhtml. Edit it to look like this:
<!– app/views/items/new.rhtml –>
<%= form_tag :action => “create” %>
Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
<%= end_form_tag %>
All these method calls generate HTML: form_tag opens a <FORM> tag, submit_tag generates a submit button, and so on. You can type out the same HTML by hand and Rails won’t care, but it’s easier to make method calls, and it makes your templates neater.
The text_field call is a little more involved. It creates an <INPUT> tag that shows up in the HTML form as a text entry field. But it also binds the value of that field to one of the members of the @item instance variable. This code creates a text entry field that’s bound to the name member of @item:
<%= text_field “item”, “name” %>
But what’s the @item instance variable? Well, it’s not defined yet, because we’re still using the generated controller. If you try to access the page /items/new page right now, you may get an error complaining about an unexpected nil value. The nil value is the @item variable, which gets used (in text_field calls) without ever being defined.
Let’s customize the ItemsController class so that the new action sets the @item instance variable properly. We’ll also implement the create action so that something actually happens when the user hits the submit button on our generated form.
class ItemsController < ApplicationController
def new
@item = Item.new
end
def create
@item = Item.create(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end
Now if you access the /items/new page, you’ll see what you’d expect: a form with two text entry fields. The “Name” field will be blank, and the “Value” field will contain the default database value of “[empty]“.
Fill out the form and submit, and a new row will be created in the items table. You’ll be redirected to the edit action, which doesn’t exist yet. Let’s create it now. Here’s the controller part (note the similarity between ItemsController#edit and ItemsController#create above):
class ItemsController < ApplicationController
def edit
@item = Item.find(params[:id])
if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end
end
In fact, the edit action is so similar to the create action that its form can be almost identical. The only differences are in the arguments to form_tag:
<!– app/views/items/edit.rhtml –>
<%= form_tag :action => “edit”, :id => @item.id %>
Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
<%= end_form_tag %>
Discussion
This is probably the most common day-to-day task faced by web developers. It’s so common that Rails comes with a tool called scaffold that generates this kind of code for you. If you’d invoked generate this way instead of with the arguments given above, Rails would have generate code for the actions given in the Solution, plus a few more:
$ ./script/generate scaffold Items
Starting off with scaffolding doesn’t mean you can get away with not knowing how Rails form generation works, because you’ll definitely want to customize the scaffolding code.
There are two places in our code where magic happens. The first is the text_field call in the view, which is explained in the Solution. It binds a member of an object (@item.name , for instance) to an HTML form control. If you view the source of the /items/new page, you will see that the form fields look something like this:
Name: <input type=”text” name=”item[name]“ value=”" /><br />
Value <input type=”text” name=”item[value]“ value=”[empty]” /><br />
These special field names are used by the second piece of magic, located in the calls to Item.create (in new) and Item#update_attributes. In both cases, an Item object is fed a hash of new values for its members. This hash is embedded into the params hash, which contains CGI form values.
The names of the HTML form fields (item[name] and item[value]) translate into a params hash that looks like this:
{
:item => {
:name => “Name of the item”,
:value => “Value of the item”
},
:controller => “items”,
:action => “create”
}
So this line of code:
Item.create(params[:item])
is effectively the same as this line:
Item.create(:name => “Name of the item”, :value => “Value of the item”)
The call to Item#update_attributes in the edit action works exactly the same way.
As mentioned above, the views for edit and new are very similar, differing only in the destination of the form. With some minor refactoring, we can remove one of the view files completely.
A call to <%= form_tag %> without any parameters at all sets the form destination to the current URL. Let’s change the new.rhtml file appropriately:
<!– app/views/items/new.rhtml –>
<%= form_tag %>
Name: <%= text_field “item”, “name”
%>

Value: <%= text_field “item”, “value”
%>

<%= submit_tag %>
<%= end_form_tag %>
Now the new.rhtml view is suitable for use by both new and edit. We just need to change the new action to call the create method (since the form doesn’t go there anymore), and change the edit action to render new.rhtml instead of edit.rhtml (which can be removed):
class ItemsController < ApplicationController
def new
@item = Item.new
create if request.post?
end
def edit
@item = Item.find(params[:id])
if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id and return
end
render :action => ‘new’
end
end
Remember from Recipe 15.5 that a render call only specifies the template file to be used. The render call in edit won’t actually call the new method, so we don’t need to worry about the new method overwriting our value of @item.
In real life, there would be enough differences in the content surrounding the add and edit forms to a separate view for each action. However, there’s usually enough similarity between the forms themselves that they can be refactored into a single partial view (see Recipe 15.14) which both views share. This is a great example of the DRY (Don’t Repeat Yourself) principle. If there is a single form for both the add and edit views, it’s easier and less error-prone to maintain that form as the database schema changes.
See Also
15.17 Creating an Ajax Form
Problem
You want to build a web application that’s responsive and easy to use. You don’t want your users to spend lots of time waiting around for the browser to redraw the screen.
Solution
You can use JavaScript to make the browser’s XMLHTTPRequest object send data to the server, without dragging the user through the familiar (but slow) page refresh. This technique is called Ajax,* and Rails makes it easy to use Ajax without writing or knowing any JavaScript.
Before you can do Ajax in your web application, you must edit your application’s main layout template so that it calls the javascript_include_tag method within its <HEAD> tag. This is the same change made in Recipe 15.15:
<!– app/views/layouts/application.rhtml –>
<html>
<head>
<title>My Web App</title>
<%= javascript_include_tag “prototype”, “effects” %>
</head>
<body>
<%= @content_for_layout %>
</body>
</html>
Let’s change the application from Recipe 15.16 so that the new action is AJAX-enabled (if you followed that recipe all the way through, and made the edit action use new.rhtml instead of edit.rhtml, you’ll need to undo that change and make edit use its own view template).
We’ll start with the view template. Edit app/views/items/new.rhtml to look like this:
<!– app/views/items/new.rhtml –>
<div id=”show_item”></div>
<%= form_remote_tag :url => { :action => :create },
:update => “show_item”,
:complete => visual_effect(:highlight, “show_item”) %>
Name: <%= text_field “item”, “name”
%><br />
Value: <%= text_field “item”, “value”
%><br />
<%= submit_tag %>
<%= end_form_tag %>
Those small changes make a standard HTML form into an Ajax form. The main difference is that we call form_remote_tag instead of form_tag. The other differences are the arguments we pass into that method.
The first change is that we put the :action parameter inside a hash passed into the :url option. Ajax forms have more options associated with them than a normal form, so you can’t describe its form action as simply as you can with form_tag.
When the user clicks the submit button, the form values are serialized and sent to the destination action (in this case, create) in the background. The create action processes the form submission as before, and returns a snippet of HTML.
What happens to this HTML? That’s what the :update option is for. It tells Rails to take the result of the form submission, and stick it into the element with the HTML ID of “show_item”. This is why we added that <div id=”show_item”> tag to the top of the template: that’s where the response from the server goes.
The last change to the new.rhtml view is the :complete option. This is a callback argument: it lets you specify a string of JavaScript code that will be run once an Ajax request is complete. We use it to highlight the response from the server once it shows up.
That’s the view. We also need to modify the create action in the controller so that when you make an Ajax form submission, the server returns a snippet of HTML. This is the snippet that’s inserted into the “show_item” element on the browser side. If you make a regular (nonAjax) form submission, the server can behave as it does in Recipe 15.16, and send an HTTP redirect.* Here’s what the controller class needs to look like:
class ItemsController < ApplicationController
def new
@item = Item.new
end
def create
@item = Item.create(params[:item])
if request.xml_http_request?
render :action => ’show’, :layout => false
else
redirect_to :action => ‘edit’, :id => @item.id
end
end
def edit
@item = Item.find(params[:id])
if request.post?
@item.update_attributes(params[:item])
redirect_to :action => ‘edit’, :id => @item.id
end
end
end
This code references a new view, show. It’s the tiny HTML snippet that’s returned by the server, and stuck into the “show_element” tag by the web browser. We need to define it:
<!– app/views/items/show.rhtml –>
Your most recently created item:<br />
Name: <%= @item.name %><br />
Value: <%= @item.value %><br />
<hr>
Now when you use http://localhost:3000/items/new to add new items to the data base, you won’t be redirected to the edit action. You’ll stay on the new page, and the results of your form submission will be displayed above the form. This makes it easy to create many new items at once.
Discussion
Recipe 15.16 shows how to submit data to a form in the traditional way: the user clicks a “submit” button, the browser sends a request to the server, the server returns a response page, and the browser renders the response page.
Recently, sites like Gmail and Google Maps have popularized techniques for sending and receiving data without a page refresh. Collectively, these techniques are called Ajax. Ajax is a very useful tool for improving your application’s response time and usability.
An Ajax request is a real HTTP request to one of your application’s actions, and you can deal with it as you would any other request. Most of the time, though, you won’t be returning a full HTML page. You’ll just be returning a snippet of data. The web browser will be sending the Ajax request in the context of a full web page (which you served up earlier) that knows how to handle the response snippet.
You can define JavaScript callbacks at several points throughout the lifecycle of an Ajax request. One callback, :complete, was used above to highlight the snippet after inserting it into the page. This table lists the other callbacks.
| Callback name | Callback description |
| :loading | Called when the web browser begins to load the remote document. |
| :loaded | Called when the browser has finished loading the remote document. |
| :interactive | Called when the user can interact with the remote document, even if it has not finished loading. |
| :success | Called when the XMLHttpRequest is completed, and the HTTP status code is in the 2XX range. |
| :failure |
Called when the XMLHttpRequest is completed, and the HTTP status code is not in the 2XX range. |
| :complete |
Called when the XMLHttpRequest is complete. If :success and/or :failure are also present, runs after they do. |
15.18 Exposing Web Services on Your Web Site
Problem
You want to offer SOAP and XML-RPC web services from your web application.
Solution
Rails comes with a built-in web service generator that makes it easy to expose a controllers actions as web services. You don’t have to spend time writing WSDL files or even really know how SOAP and XML-RPC work.
Here’s a simple example. First, follow the directions in Recipe 15.16 to create a database table named items, and to generate a model for that table. Don’t generate a controller.
Now, run this from the command line:
./script/generate web_service Item add edit fetch
create app/apis/
exists app/controllers/
exists test/functional/
create app/apis/item_api.rb
create app/controllers/ item_controller.rb
create test/functional/ item_api_test.rb
This creates an item controller that supports three actions: add, edit, and fetch. But instead of web application actions with .rhtml views, these are web service actions that you access with SOAP or XML-RPC.
A Ruby method doesn’t care about the data types of the objects it accepts as arguments, or the data type of its return value. But a SOAP or XML-RPC web service method does care. To expose a Ruby method through a SOAP or XML-RPC interface, we need to define type information for its signature. Open up the file app/apis/ item_api.rb and edit it to look like this:
class ItemApi < ActionWebService::API::Base
api_method :add, :expects => [:string, :string], :returns => [:int]
api_method :edit, :expects => [:int, :string, :string], :returns => [:bool]
api_method :fetch, :expects => [:int], :returns => [Item]
end
Now we need to implement the actual web service interface. Open app/controllers/ item_controller.rb and edit it to look like this:
class ItemController < ApplicationController
wsdl_service_name ‘Item’
def add(name, value)
Item.create(:name => name, :value => value).id
end
def edit(id, name, value)
Item.find(id).update_attributes(:name => name, :value => value)
end
def fetch(id)
Item.find(id)
end
end
Discussion
The item controller now implements SOAP and XML-RPC web services for the items table. This controller can live alongside an items controller that implements a traditional web interface.*
The URL to the XML-RPC API is http://www.yourserver.com/item/api, and the URL to the SOAP API is http://www.yourserver.com/item/service.wsdl. To test these services, here’s a short Ruby script that calls the web service methods through a SOAP client:
require ’soap/wsdlDriver’
wsdl = http://localhost:3000/item/service.wsdl
item_server = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_driver
item_id = item_server.add(’foo’, ‘bar’)
if item_server.edit(item_id, ‘John’, ‘Doe’)
puts ‘Hey, it worked!’
else
puts ‘Back to the drawing board…’
end
# Hey, it worked!
item = item_server.fetch(item_id)
item.class # => SOAP::Mapping::Object
item.name # => “John”
item.value # => “Doe”
Here’s the XML-RPC equivalent:
require ‘xmlrpc/client’
item_server = XMLRPC::Client.new2(’http://localhost:3000/item/api’)
item_id = item_server.call(’Add’, ‘foo’, “bar”)
if item_server.call(’Edit’, item_id, ‘John’, ‘Doe’)
puts ‘Hey, it worked!’
else
puts ‘Back to the drawing board…’
end
# Hey, it worked!
item = item_server.call(’Fetch’, item_id)
# => {”name”=>”John”, “id”=>2, “value”=>”Doe”}
item.class # => Hash
See Also
15.19 Sending Mail with Rails
Problem
You want to send an email from within your Rails application: perhaps a confirmation of an order, or notification that some action has been taken on a user’s behalf.
Solution
The first is to generate some mailer infrastructure. Go to the application’s base directory and type this command:
./script/generate mailer Notification welcome
exists app/models/
create app/views/notification
exists test/unit/
create test/fixtures/notification
create app/models/notification.rb
create test/unit/ notification_test.rb
create app/views/notification/ welcome.rhtml
create test/fixtures/notification/ welcome
We’re giving the name “Notification” to the mailing center of the application; it’s some-what analogous to a controller in the web interface. The mailer is set up to generate a single email, called “welcome”: this is analagous to an action with a view template.
Now open app/models/notification.rb and edit it to look like this:
class Notification < ActionMailer::Base
def welcome(user, sent_at=Time.now)
@subject = ‘A Friendly Welcome’
@recipients = user.email
@from = ‘admin@mysite.com’
@sent_on = sent_at
@body = {
:user => user,
:sent_on => sent_at
}
attachment ‘text/plain’ do |a|
a.body = File.read(’rules.txt’)
end
end
end
The subject of the email is “A Friendly Welcome”, and it’s sent to the user’s email address from the address “admin@mysite.com”. It’s got an attachment taken from the disk file rules.txt (relative to the root directory of your Rails application).
Although the file notification.rb is within the models/ directory, it acts like a controller in that each of its email messages has an associated view template. The view for the welcome email is in app/views/notification/welcome.rhtml, and it acts almost the same as the view of a normal controller.
The most important difference is that mailer views do not have access to the instance variables of the mailer. To set instance variables for mailers, you pass a hash of those variables to the body method. The keys become instance variable names and the values become their values. In notification.rb , we make two instance variables available to the welcome view, @user and @sent_on. Here’s the view itself:
<!– app/views/notification/welcome.rhtml –>
Hello, <%= @user.name %>, and thanks for signing up at <%= @sent_on
%>. Please print out the attached set of rules and keep them in a
prominent place; they help keep our community running smoothly. Be
sure to pay special attention to sections II.4 (”Assignment of
Intellectual Property Rights”) and XIV.21.a (”Dispute Resolution
Through Ritual Combat”).
To send the welcome email from your Rails application, add the following code to either a controller, a model, or an observer:
Notification.deliver_welcome(user)
Here, the user variable can be any object that implements #name and #email, the two methods called in the welcome method and in the template.
Discussion
You never call the Notification#welcome method directly. In fact, Notification#welcome is not even available, since it’s an instance method, and you never instantiate a Notification object directly. The ActionMailer::Base class defines a method_missing implementation that looks at all calls to undefined class methods. This is why you call deliver_welcome even though you never defined it.
The welcome.rhtml template given above generates plaintext email. To send HTML emails, simply add the following code to Notification#welcome:
content_type ‘text/html’
Now your templates can generate HTML; email clients will recognize the format of the email and render it appropriately.
Sometimes you’ll want more control over the delivery process–for example, when you’re unit-testing your ActionMailer classes. Instead of calling deliver_welcome to send out an email, you can call create_welcome to get the email as a Ruby object. These “create” methods return TMail objects, which you can examine or manipulate as necessary.
If your local web server is incapable of sending email, you can modify environment.rb to contact a remote SMTP server:
Rails::Initializer.run do |config|
config.action_mailer.server_settings = {
:address => ’someserver.com’,
:user_name => ‘uname’,
:password => ‘passwd’,
:authentication => ‘cram_md5′
}
end
See Also
Please check back tomorrow for the conclusion to this article.
—
by O’Reilly Media
The first half of this tutorial introduced you to making a rudimentary but functional widget of the sort you can find on Yahoo’s site since its purchase of Konfabulator. In this article, you’ll add the finishing touches to increase its functionality.Widget Walkthrough
The preferences that you created in the first part of this article won’t actually do anything on their own; they’ll need the following JavaScript in order to actually make the required changes:
<action trigger=”onLoad”>
<![CDATA[
function updatepreferences() {
datatextarea.font = preferences.textfontpref.value;
datatextarea.color = preferences.textcolorpref.value;
datatextarea.size = preferences.textsizepref.value;
datatextarea.style = preferences.textstylepref.value;
}
updatepreferences();
]]>
</action>
<action trigger =”onPreferencesChanged”>
updatepreferences();
</action>
As you can see, each action (onLoad and onPreferencesChanged) is contained within its own <action> element. All each line of the updatepreferences() function is doing is setting the attributes of the textarea element, much as CSS would with HTML. I’ve included the opening CDATA tag as we’ll need it for one of the next functions.
Widget Walkthrough – Getting the headlines
Now you need to actually get the news headlines from the BBC web server. To do this, you’ll need to make use of the URL widget engine object:
function getdata() {
var url = new URL();
url.location =
“http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/
technology/rss.xml”;
feeddata = url.fetch();
}
getdata();
This will instruct the widget to fetch whatever file is waiting at the above URL. You now need to display the file:
function setdata() {
datatextarea.data = feeddata;
}
setdata();
This function can be added into the existing onLoad <action> and simply creates a new URL object, sets the location attribute to the source of the feed, and then retrieves the information as the specified location. At this point, you should have something that looks a little like this:

The function works; it grabs the file located at the target URL, but unfortunately, this happens to be in an XML format! This means our textarea is currently displaying the entire XML file! Another function, to traverse the XML document and pull out just the headlines, is going to be needed. Fortunately, the widget engine provides XPath 1.0 support to make extracting the required items relatively easy.
There are several steps involved in getting the data that we want; if you look at the XML document obtained by the url.fetch command, you’ll see that the news headlines we want to grab appear in an element called title, which is a child of the item element, which is a child of the channel element, which finally, is a child of the rss element. Therefore, the element we need is a great-grandchild of the root element. Remove the setData() function as we can extend the getData() function to display the headlines once they have been extracted.
Widget Walkthrough – Using loops
The Widget reference manual states that you should use a try catch loop to perform XPath functions, so you can add one to your getData() function. You will first need to obtain the XML document and place it in a variable using the parse method:
try {
doc = XMLDOM.parse(feeddata);
Next, you need to specify which elements in the XML file you want to match. This is the XPath element of your function:
titlelist = doc.evaluate( “rss/channel/item/title” );
datatextarea.data = “”;
You now need to create an array to hold the headlines as separate DOMElement objects:
titles = new Array();
In this particular rss file, there are always 19 headlines, however, if you were using a different feed, you may not know the number of headlines in advance so it is a good idea to use a for loop that operates on the length of your array:
for (n = 0; n < titlelist.length; n++) {
The DOMNodeList returned by the XPath function has a built-in property of item() which can be used to specify which DOMElement in the list you are referring to. In this case, we can match the number of the array item with the item we want to store in the array:
titles[n] = titlelist.item(n);
Finally, each time the loop iterates, you can set the data property of the <textarea> to the data held in the array item. The actual item held in the array is the DOMElement; to get the actual text held in the object you need to address the firstChild of the element. The JavaScript new line character is also specified (twice) to break up the headlines:
datatextarea.data += titles[n].firstChild.data + “nn”;
}
}
catch(e) {
print(e);
}
Widget Walkthrough – Fine tuning headline retrieval
If you save the changes and reload the file now, the headlines should appear as if by magic! Using the <textarea> is good because it makes setting the preferences easy and handles the word wrapping well. One major flaw of this though is that it’s not possible to set the URL of each individual headline. To compensate for this, you can add a right-click menu item to the widget that takes you to either the main news front page, or a page containing the list of headlines displayed. To do this, you can add the following code block to the <textarea> element:
<contextMenuItems>
<menuItem title=”Open BBC Technology News”>
<onSelect>openURL
(”http://news.bbc.co.uk/1/hi/technology/default.stm”);</onSelect>
</menuItem>
<menuItem title=”Open the actual headlines page”>
<onSelect>openURL
(http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/technology/rss.xml);
</onSelect>
</menuitem>
</contextMenuItems>
This at least provides you with a way of reading the full news stories. Another thing that you need to consider is a way of updating the file used as the source of the data; if the computer and widget are left running, the reader will just display the same data, forever. A timer element can be used to specify that an action could be carried out repeatedly on a set interval:
<timer>
<interval>10</interval>
<onTimerFired>getdata();</onTimerFired>
</timer>
The interval is measured in seconds, but this kind of widget wouldn’t really need to grab a new rss file every ten seconds. Depending on the frequency of updates at the source, you could probably set it to update maybe several times daily.

Widget Walkthrough – Publishing your widget
In order to get your widget published on the Yahoo! Widget Gallery, you need to switch off debugging and package your widget. Right at the top of the file, you’ll find the <debug> element; set this to off. To package the application and produce the flat, one-file version of the existing files and folder structure, you’ll need to get to grips with the command line interface (CLI) included in the SDK. The converter is situated (on a Windows XP system) at the following location:
C:Program FilesYahoo!Yahoo! Widget Engine
You need to create an output directory at this stage so create a folder, also at the above location, called feeder or something similar. Now open a command prompt. It will probably open (on a Windows XP system) in your My Document folder so you’ll need to use the cd (change directory) command to browse to the location of the CLI. You should also move your widget folder into the same directory as the CLI and output directory. Once this has been done, use the following command to package your creation:
converter_4a convert -v -flat TechFeeder.widget -o outputDirName
You should then end up with a flat file package containing all of the files that make up the widget in the output folder. Your widget should now be ready to go onto the gallery, but don’t submit this one (because I already have).
The tagline displayed on the home page of the Widget gallery is “The grass is greener on both sides” which pays homage to widgets’ cross platform compatibility. When creating widgets, you should ensure that you test your widget on a Mac and that it runs on both Mac and Windows platforms. I have tested this widget on a Mac and unfortunately, it doesn’t work as well as it does on Windows systems; instead, it just displays the first headline. I don’t actually know why this is, but I am investigating. In the meantime however, I’ve specified on the gallery that it’s Windows only. Also, the Widget optimizer tool is supposed to be used to remove unnecessary memory forks on widget created on the Mac, but like the converter tool it isn’t currently available, so for the time being, those of you on Macs I guess will have to forgo this at present.
There is a lot more that could be done to refine the widget. At this stage it really is just a version 1.0 release. You could add a function that automatically scrolls the headlines perhaps, or create the headlines as individual text elements, each with their own URL property to make them clickable links to each of the news stories, or even include the description in a title element that displays when the mouse runs over each text element; the possibilities are endless. But what you have now is a very basic, but fully functional widget, produced with ease and in not much time at all. This is the beauty of the Yahoo! Widget Engine, speed and ease of deployment and fully functional information management right on your desktop.
11 Sep
Posted by ProCOM
on September 11, 2007 – 9:46 am - 719 views
An XML data island is a piece of well-formed XML embedded into an HTML file. This article will show you how to retrieve data in an XML format from a database using ADO; you will also learn how to bind this data into an HTML document.Introduction
The previous tutorial, The Why and How of XML Data Islands, considered embedding a well formed XML fragment into an HTML file to create an XML data island. The article also showed how one could access data embedded in the XML file. The tutorial also described data binding to various HTML tags. However the XML data used was a hard-coded XML fragment.
In this tutorial, how data in XML format can be retrieved from a database using ADO will be described. The XML data obtained using ADO will be reviewed. An example of how Jet Data types are associated with XML data types will be shown. This will be followed by how to use the data to bind it to an HTML document. After all, this is what the client is after. It will be helpful if the reader reviews the previous article and the XML related articles to which you can find links on this site.
Data to XML Conversion using ADO
Persisting data in the XML format is one of the most important features of ADO since the 2.5 version. This means that ADO recordsets can be saved as an XML file to a location of your choice. Alternatively they can also be saved to a Stream object. Since the data is derived from a database together with the data, the data structure comes with it. Ideally this format should be able to be transparently used by any machine.
Displaying ADO Retrieved Data with XML Islands – Extracting XML formatted data example
Using the save() method of ADO, you could save the recordset to a file as shown in the next paragraph. The recordset is created by accessing an MDB file on the hard drive. Not all the columns in the ‘Employees‘ table in the Northwind database will be saved as an XML file. The variable fileName points to the location on the hard drive where the XML file will be saved.

Create UI to test code
On a form in your MS Access application add a button, and to the click event of this button add the following code. The statement
rs.save fileName, adPersistXML >
can also be saved as
rs.save fileName2, adPersistADTG
where fileName2 will have an .adtg extension. This is yet another proprietary format called the Advanced Data TableGram format. We will only look at the persisted XML formatted file.
Private Sub Command0_Click()
Dim rs As New ADODB.Recordset
fileName = "C:NwindEmployees.xml"
rs.Open "Select * from Employees where LastName='Peacock'", _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:Documents and SettingsJayMy DocumentsRetrieve.mdb;" & _
"Persist Security Info=False", adOpenKeyset, adLockOptimistic,
adCmdText
If Dir$(fileName) <> "" Then Kill fileName
rs.Save fileName, adPersistXML
End Sub
In the above code the recordset for the indicated SQL statement will be saved.
Displaying ADO Retrieved Data with XML Islands – The Saved XML file
If you open the file C:NwindEmployees.xml in a text editor you can see the full details.
<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
xmlns:rs='urn:schemas-microsoft-com:rowset'
xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
<s:ElementType name='row' content='eltOnly' rs:updatable='true'>
<s:AttributeType name='Address' rs:number='8'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='Address'>
<s:datatype dt:type='string' dt:maxLength='60'/>
</s:AttributeType>
<s:AttributeType name='BirthDate' rs:number='6'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='BirthDate'>
<s:datatype dt:type='DateTime'rs:dbtype='variantdate' dt:maxLength='16' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='City' rs:number='9'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='City'>
<s:datatype dt:type='string' dt:maxLength='15'/>
</s:AttributeType>
<s:AttributeType name='Country' rs:number='12'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='Country'>
<s:datatype dt:type='string' dt:maxLength='15'/>
</s:AttributeType>
<s:AttributeType name='EmployeeID' rs:number='1'rs:maydefer='true' rs:writeunknown='true' rs:basetable='Employees'
rs:basecolumn='EmployeeID'rs:autoincrement='true'>
<s:datatype dt:type='int' dt:maxLength='4'rs:precision='10' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='Extension' rs:number='14'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='Extension'>
<s:datatype dt:type='string' dt:maxLength='4'/>
</s:AttributeType>
<s:AttributeType name='FirstName' rs:number='3'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='FirstName'>
<s:datatype dt:type='string' dt:maxLength='10'/>
</s:AttributeType>
<s:AttributeType name='HireDate' rs:number='7'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='HireDate'>
<s:datatype dt:type='dateTime'rs:dbtype='variantdate' dt:maxLength='16' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='HomePhone' rs:number='13'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='HomePhone'>
<s:datatype dt:type='string' dt:maxLength='24'/>
</s:AttributeType>
<s:AttributeType name='LastName' rs:number='2'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='LastName'>
<s:datatype dt:type='string' dt:maxLength='20'/>
</s:AttributeType>
<s:AttributeType name='Notes' rs:number='16'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='Notes'>
<s:datatype dt:type='string'dt:maxLength='536870910' rs:long='true'/>
</s:AttributeType>
<s:AttributeType name='Photo' rs:number='15'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='Photo'>
<s:datatype dt:type='string' dt:maxLength='255'/>
</s:AttributeType>
<s:AttributeType name='PostalCode' rs:number='11'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='PostalCode'>
<s:datatype dt:type='string' dt:maxLength='10'/>
</s:AttributeType>
<s:AttributeType name='Region' rs:number='10'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='Region'>
<s:datatype dt:type='string' dt:maxLength='15'/>
</s:AttributeType>
<s:AttributeType name='ReportsTo' rs:number='17'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='ReportsTo'>
<s:datatype dt:type='int' dt:maxLength='4'rs:precision='10' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='Title' rs:number='4'rs:nullable='true' rs:maydefer='true' rs:write='true'rs:basetable='Employees'
rs:basecolumn='Title'>
<s:datatype dt:type='string' dt:maxLength='30'/>
</s:AttributeType>
<s:AttributeType name='TitleOfCourtesy'rs:number='5' rs:nullable='true' rs:maydefer='true' rs:write='true'
rs:basetable='Employees'rs:basecolumn='TitleOfCourtesy'>
<s:datatype dt:type='string' dt:maxLength='25'/>
</s:AttributeType>
<s:extends type='rs:rowbase'/>
</s:ElementType>
</s:Schema>
<rs:data>
<z:row Address='4110 Old Redmond Rd.'BirthDate='1958-09-19T00:00:00' City='Redmond' Country='USA'EmployeeID='4'
Extension='5176' FirstName='Margaret'HireDate='1993-05-03T00:00:00' HomePhone='(206) 555-8122'LastName='Peacock'
Notes='Margaret holds a BA in English literature fromConcordia College and an MA from the American Institute of Culinary Arts.She was temporarily assigned to the London office before returning to her permanent post in Seattle.'
Photo='EmpID4.bmp' PostalCode='98052' Region='WA'ReportsTo='2' Title='Sales Representative' TitleOfCourtesy='Mrs.'/>
</rs:data>
</xml>
Displaying ADO Retrieved Data with XML Islands – Reviewing the saved file
Although the file is large, it consists basically of two parts, as shown in the browser display of this file after collapsing all the details. As seen in the next picture, the file’s two parts are the ‘Schema’ and the ‘Data’ represented by their prefixes, ‘s:‘ and ‘rs:‘ as shown in the namespaces — the first four lines in the document which have the prefix xmlns.

The schema section
The next picture shows just one element from the expanded ‘s‘ node in the displayed XML file in the browser. You can also see that it is updatable. This particular slice corresponds to the ‘Address‘ field of the Employees table shown in the first picture. The fields are listed alphabetically in the persisted file. The other elements also show the various attributes of the Address field. In the original table, the Address field’s Data type is text and field size is 60. The XML attribute with the prefix ‘dt’ which marks the beginning of each row shows this information. The ‘text’ has become ’string’ and the field size has become ‘maxlength.’ The schema information therefore is an accurate representation of the data structure.

The Data Section
The next section shows the only row of data taken from the data section. The prefix ‘z’ marks the beginning of the data. The XML file has only one row of the table returned corresponding to the LastName=’Peacock.‘

Displaying ADO Retrieved Data with XML Islands – Data types in Access 2003 and XML file
In MS Access there are several data types that are typical to its Jet Library. In order to look at how the data goes over into XML, a table, called ‘Whimsical‘ was contrived which has all the data types but contains only a single row of data. This was opened just like the previous table and the saved file was examined. In the next picture you see the table and in the one that follows, you see the ’schema’ section. The data types, ‘bitmap’, and ‘hyperlink’ can take up a large amount of space. This one row of data saved to file takes up as much as 0.5 MB.

This is the ‘Schema’ section of the above file. Review each of the data types and you’ll see the corresponding dt:type in the XML file.

Displaying ADO Retrieved Data with XML Islands – Displaying retrieved XML in an HTML document
Creating an XML Data Island
From the previous tutorial we know that we need to embed the XML in an XML document to produce the XML Data Island. The ADO’s save() method does not produce a data island. This can be built in two steps. First of all, to associate the ‘Data’ contained in the XML to the bondable tags of the HTML, we need a basis of association. This is given by the following XML block with the id=’test.’
<XML id="test">
</XML>
In the second step, you will notice that XML is already the first tag in the saved file. Since you cannot have two XML tags, you modify the saved file by prefixing ado to xml and changing it to adoxml as shown. This will be embedded in the previous block and the resulting XML is the XML Data Island.
<adoxml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
xmlns:rs='urn:schemas-microsoft-com:rowset'
xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
<s:ElementType name='row' content='eltOnly' rs:updatable='true'>
<s:AttributeType name='Address' rs:number='8'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='Address'>
<s:datatype dt:type='string' dt:maxLength='60'/>
</s:AttributeType>
<s:AttributeType name='BirthDate' rs:number='6'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='BirthDate'>
<s:datatype dt:type='dateTime'rs:dbtype='variantdate' dt:maxLength='16' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='City' rs:number='9'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='City'>
<s:datatype dt:type='string' dt:maxLength='15'/>
</s:AttributeType>
<s:AttributeType name='Country' rs:number='12'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='Country'>
<s:datatype dt:type='string' dt:maxLength='15'/>
</s:AttributeType>
<s:AttributeType name='EmployeeID' rs:number='1'rs:maydefer='true' rs:writeunknown='true' rs:basetable='Employees'
rs:basecolumn='EmployeeID'rs:autoincrement='true'>
<s:datatype dt:type='int' dt:maxLength='4'rs:precision='10' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='Extension' rs:number='14'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='Extension'>
<s:datatype dt:type='string' dt:maxLength='4'/>
</s:AttributeType>
<s:AttributeType name='FirstName' rs:number='3'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='FirstName'>
<s:datatype dt:type='string' dt:maxLength='10'/>
</s:AttributeType>
<s:AttributeType name='HireDate' rs:number='7'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='HireDate'>
<s:datatype dt:type='dateTime'rs:dbtype='variantdate' dt:maxLength='16' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='HomePhone' rs:number='13'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='HomePhone'>
<s:datatype dt:type='string' dt:maxLength='24'/>
</s:AttributeType>
<s:AttributeType name='LastName' rs:number='2'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='LastName'>
<s:datatype dt:type='string' dt:maxLength='20'/>
</s:AttributeType>
<s:AttributeType name='Notes' rs:number='16'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='Notes'>
<s:datatype dt:type='string'dt:maxLength='536870910' rs:long='true'/>
</s:AttributeType>
<s:AttributeType name='Photo' rs:number='15'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='Photo'>
<s:datatype dt:type='string' dt:maxLength='255'/>
</s:AttributeType>
<s:AttributeType name='PostalCode' rs:number='11'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='PostalCode'>
<s:datatype dt:type='string' dt:maxLength='10'/>
</s:AttributeType>
<s:AttributeType name='Region' rs:number='10'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='Region'>
<s:datatype dt:type='string' dt:maxLength='15'/>
</s:AttributeType>
<s:AttributeType name='ReportsTo' rs:number='17'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='ReportsTo'>
<s:datatype dt:type='int' dt:maxLength='4'rs:precision='10' rs:fixedlength='true'/>
</s:AttributeType>
<s:AttributeType name='Title' rs:number='4'rs:nullable='true' rs:maydefer='true' rs:write='true' rs:basetable='Employees'
rs:basecolumn='Title'>
<s:datatype dt:type='string' dt:maxLength='30'/>
</s:AttributeType>
<s:AttributeType name='TitleOfCourtesy' rs:number='5'rs:nullable='true' rs:maydefer='true' rs:write='true'
rs:basetable='Employees'rs:basecolumn='TitleOfCourtesy'>
<s:datatype dt:type='string' dt:maxLength='25'/>
</s:AttributeType>
<s:extends type='rs:rowbase'/>
</s:ElementType>
</s:Schema>
<rs:data>
<z:row Address='4110 Old Redmond Rd.'BirthDate='1958-09-19T00:00:00' City='Redmond' Country='USA' EmployeeID='4'
Extension='5176' FirstName='Margaret'HireDate='1993-05-03T00:00:00' HomePhone='(206) 555-8122' LastName='Peacock'
Notes='Margaret holds a BA in English literature fromConcordia College and an MA from the American Institute of Culinary Arts. She was temporarily assigned to the London office before returning to her permanent post in Seattle.'
Photo='EmpID4.bmp' PostalCode='98052' Region='WA'ReportsTo='2' Title='Sales Representative' TitleOfCourtesy='Mrs.'/>
</rs:data>
</adoxml>
Displaying ADO Retrieved Data with XML Islands – Creating an HTML document which can display the XML Data
AdoIsland.html
In the code shown next, insert the XML data island created in the previous section and save it as AdoIsland.htm. Make sure the DATASRC attribute of the table corresponds to the ID field of the XML Data Island. The DATAFIELD attribute refers respectively to the data fields in the XML data island.
<html><head><title></title></head>
<body>
<table DATASRC="#Test">
<tr>
<tr><td>
<table DATAsrc="#Test" DATAFLD="rs:data" border="1"
bgcolor="yellow">
<tr><td>
<table DATAsrc="#Test" DATAFLD="z:row">
<tr>
<td><span DATAFLD="EmployeeID"></span</td>
<td><span DATAFLD="FirstName"></span</td>
<td><span DATAFLD="LastName"></span</td>
</tr>
</table>
</td></tr>
</table>
</td></tr>
</table>
<!--Here plug in the XML Data Island -->
</body>
</html>
Displaying the XML Data retrieved on the IE
Now if you browse the AdoIsland.htm file the display should appear as follows. If you refer to the table above, you can see that although all fields are in the XML Data Island, only three of them are called while displaying.

Displaying the image saved as an embedded BMP file
From the XML file you could retrieve the field for the ‘Photo’ and display the image on the browser. For this, you need to add another cell to the table and insert the following for the cell.
<td><img src="" DATAFLD="Photo"></td>
With the above table cell added to the table, the display now appears as shown here. For this to display correctly you should have the corresponding BMP file at the same location as the AdoIsland.htm file.

Summary
This tutorial extends the previous tutorial to show how to access data and display it using the ADO’s save() method. An example to display the persisted data using the XML Data Island was described. More importantly, the data types in MS Access as they relate to data types in the persisted XML file were shown in detail. Similar ideas will help in accessing data from a web server with Active Server Pages as well.
Widget Walkthrough
A widget should be created to fill a need; it should actually do something useful. What I decided on in the end was a news reader. Every day, when I turn on my computer, one of the first things I do is to go to the BBC news website and check out the technology section headlines. I decided that my news reader would take the RSS feed supplied by the BBC and list the daily technology headlines. Thus the idea for TechFeeder was born.
The very first thing that I did was draw the main element of my widget in Photoshop (any decent graphic tool will do, but you won’t be able to use the Photoshop script if you use a different application). I won’t go into extreme detail over exactly what I did, but the tutorial I followed advised that aqua style icons are made using a layered mixture of shading, opacity and lighting effects. There are plenty of guides out there on aquifying pictures so if you’ve never created one before, I’d recommend searching for one and practicing a bit before the main event.
It took a couple of attempts before I had something that looked like I wanted it to, but soon enough I had the basic appearance that I had envisioned and settled with it. I think you’ll agree that while it isn’t perfect and has obvious flaws, it works reasonably well. Perhaps I’ll improve it for release 1.1 of my widget! While you have Photoshop open, you may as well create the image that you’ll use for the about-box for your widget. The about box should match the style of your widget if possible and list things such as the version number, author details and anything else you feel is appropriate. You can create the whole thing in Photoshop or just the background and use XML to add the text. It’s up to you, but in order to demonstrate some of the about-box capabilities, I just created the background in my image editor.


When you run the widget creation.jsx file, you’ll be asked to submit your name, the widget version and choose a location for the widget directory to be created. Once it has finished, you’ll need to go into the contents folder and open the .kon file in a text editor.
You should be presented with some code that looks very similar to this:
<?xml version=”1.0″ encoding=”UTF-8″?>
<widget version=”1.0″ minimumVersion=”2.0″ author=”Dan Wellman”>
<debug>on</debug>
<window title=”TechFeeder”>
<name>mainWindow</name>
<width>206</width>
<height>141</height>
<visible>1</visible>
<shadow>0</shadow>
</window>
<image src=”Resources/Shape 1.png”>
<name>Shape_1</name>
<hOffset>0</hOffset>
<vOffset>5</vOffset>
<width>206</width>
<height>136</height>
<opacity>70%</opacity>
</image>
This should make sense at first glance to anyone who’s worked with XML before. The XML declaration comes first, followed by the <widget> tag which is the container into which all other tags must be placed. Next is the <window> tag, which in this case specifies the main window. Notice that the image that makes up the main background of the widget is specified in its own <image> block, separate from (not nested within) the window element. To make the widget semi-transparent, as many are, I’ve lowered the opacity of the image. I have used a percentage here, but you could also use an integer from 0 to 255 to specify the opacity.
Introduction to Widgets – Adding Your Own Code
Now you need to start adding code yourself. What I focused on first was the about-box, which is an element used solely to display a little window listing the program version, the creator, and anything else as a programmer that you want or need to display. As I’m using content from the BBC site, I felt it necessary to include their copyright information:
<about-box>
<image>Resources/about-backg.png</image>
<about-version>
<font>Arial</font>
<size>12</size>
<style>bold</style>
<hOffset>90</hOffset>
<vOffset>45</vOffset>
<color>#ffffff</color>
</about-version>
<about-text>
<data>BBC TechFeeder</data>
<font>Arial</font>
<size>18</size>
<style>bold</style>
<color>#ffffff</color>
<hOffset>90</hOffset>
<vOffset>30</vOffset>
</about-text>
<about-text>
<data>Copyright:(C)</data>
<font>Arial</font>
<size>12</size>
<style>bold</style>
<color>#ffffff</color>
<hOffset>90</hOffset>
<vOffset>90</vOffset>
</about-text>
<about-text>
<data>British Broadcasting Corporation</data>
<font>Arial</font>
<size>10</size>
<style>bold</style>
<color>#ffffff</color>
<hOffset>90</hOffset>
<vOffset>105</vOffset>
</about-text>
<about-text>
<data>Click here for terms and conditions</data>
<url>http://news.bbc.co.uk/1/hi/help/rss/4498287.stm</url>
<font>Arial</font>
<size>10</size>
<style>bold</style>
<color>#ffffff</color>
<hOffset>90</hOffset>
<vOffset>120</vOffset>
</about-text>
<about-text>
<data>of reuse.</data>
<font>Arial</font>
<size>10</size>
<style>bold</style>
<color>#ffffff</color>
<hOffset>90</hOffset>
<vOffset>130</vOffset>
</about-text>
<about-text>
<data>By Dan Wellman</data>
<font>Arial</font>
<size>14</size>
<style>bold</style>
<color>#ffffff</color>
<hOffset>90</hOffset>
<vOffset>185</vOffset>
</about-text>
<about-text>
<data>2006</data>
<font>Arial</font>
<size>12</size>
<style>bold</style>
<color>#ffffff</color>
<hOffset>90</hOffset>
<vOffset>200</vOffset>
</about-text>
</about-box>
It’s a whopping amount of code for one small window, most of which is graphic, but it’s easy code and should make absolute sense at a glance. The about-version code actually pulls the version number from the main kon file at run time. The reason it’s so large is that at present, text in the about-box doesn’t wrap and is simply cut off at the end of the window. This is why there are so many <about-text> blocks; each line is its own separate object. The <about-version> and <about-text> blocks are listed separately, much like the <image> element above. Note that the about box must feature an image in order to function and that the bit of the text that is a hyperlink must have the <url> attribute set in the relevant code block. Once complete, the about box will appear like this:

Introduction to Widgets – Displaying Data
The application will be getting information from a news feed and it is going to need to display the information somewhere in order to carry out its function. I’ve created a text area to display the data for its multi-line facility, and I’ve enclosed it in a frame element primarily because of the automatic scrollbar capabilities that this element features:
<frame name=”dataframe”>
<textarea name=”datatextarea”>
<data>Loading…</data>
<font>Arial</font>
<editable>false</editable>
<color>#000000</color>
<size>12</size>
<style>bold</style>
<height>80</height>
<width>150</width>
<voffset>35</voffset>
<hoffset>30</hoffset>
<bgcolor>#cccccc</bgcolor>
<bgopacity>0</bgopacity>
</textarea>
</frame>
If you set the data element to hold the text “Loading…” this is what will be displayed when the widget first appears on screen. This is important because it takes a few seconds before the data is pulled through and parsed from the RSS feed. You’ll see in one of the functions later on that the data property of the text area is loaded after the source file has been obtained.
It’s great that the scroll bar handles, track and face are all drawn for you with absolutely no additional coding when using a frame element. You can also specify a frame element that will override this automaton and can be controlled via script. Doing this is a bit beyond the scope of this article, but such an element could be used as part of a function that automatically scrolls the text displayed in the text area.
Introduction to Widgets – Setting the Preferences
Next, I tackled the preferences. The widget engine will draw the preferences box for you and include the common options such as locking the widget’s position and setting the opacity. All you have to do is specify any additional options, and write the JavaScript to make them work:
<preference>
<name>textfontpref</name>
<defaultValue>Arial</defaultValue>
<title>Text Font:</title>
<type>font</type>
<description>Select a font for the news text.</description>
</preference>
<preference>
<name>textcolorpref</name>
<defaultvalue>#000000</defaultvalue>
<title>Text Color:</title>
<type>color</type>
<defaultValue>#000000</defaultValue>
<description>Select a colour for the news text.</description>
</preference>
<preference>
<name>textsizepref</name>
<defaultvalue>12</defaultvalue>
<title>Text Size:</title>
<type>popup</type>
<option>8</option>
<option>10</option>
<option>12</option>
<option>14</option>
<description>Select a font size for the news text.</description>
</preference>
<preference>
<name>textstylepref</name>
<defaultvalue>Bold</defaultvalue>
<title>Text Color:</title>
<type>popup</type>
<option>Bold</option>
<option>Italic</option>
<option>Narrow</option>
<option>Expanded</option>
<option>Condensed</option>
<option>Smallcap</option>
<option>Poster</option>
<option>Compressed</option>
<option>Fixed</option>
<option>No Style</option>
<description>Select a text style for the news text (it will only be applied if
supported by the font).</description>
</preference>
Widgets should be as customizable as possible; therefore, I’ve given the user control over everything that isn’t an image, which in this case is just the text from the BBC news feed. Each property you are able to change has its own segment of code. The <type> attributes of the first two preferences are used by the engine to automatically create a font chooser that previews all of your installed fonts, and a color picker, as seen in other applications.
In part two of this article I’ll show you how to wire up these preferences with a little bit of script to actually make them work. We’ll also work on the main script that makes the whole thing tick, and look at packaging and preparing the widget for upload to the online gallery.
—
By Dan Wellman
07 Sep
Posted by ProCOM
on September 7, 2007 – 7:36 pm - 691 views
The twin tasks of the XMLHttp are handling the HTTP request, and then processing the XML response. The first one is easily done by writing the appropriate syntax for creating this object. This is what is accomplished in AJAX by invoking the new constructor for XMLHTTP object. This is but one of the objects of the XMLDOM -XML Document Object Model. In this article, we look at XMLDOM in some detail before calling the XML document in an AJAX rendition in a future article.
A quick look at XMLDOM
The DOM of HTML and XMLDOM share some common traits. The basic precept that documents are hierarchically structured with nodes and nested nodes is common to both of them. The DOM object model as seen in W3C documentation is described in full here.
XMLDOM can be understood through two types of metaphors, a tree representation and a family representation. In the tree representation, the tree has nodes. Nodes can have other nodes growing out of them, or a node can stand by itself without any other nodes sprouting from it. In the family metaphor you have parents, children, siblings, and so on.
Accessing XML via XMLDOM is facilitated by ActiveX. All that is needed is to create a new ActiveXObject, via the call:
var xml_doc=new ActiveXObject (”Microsoft.XMLDOM”);
Once this is created, an XML document from a given location can be loaded. Then we can look at the various properties of this object as detailed in the XMLDOM. The following listing gives a brief description of the XMLDOM objects that are well suited for client side scripting.
In XMLDOM as well as HTML’s DOM the following interfaces are used for accessing. In the present tutorial we will only look at a subset of the properties and methods of the highlighted objects.
Document
Document is the root of the XML document.
ElementThe element object represents the Document’s various elements, such as root and other elements.
Node“Node” is a single node in the document tree representation. This has support for data types, namespaces, DTDs and XML Schemas.
Attribute“Attribute” represents the single attribute node of an element.
Text“Text” represents the text of an element or attribute.
Roaming through XMLDOM: An AJAX Prerequisite – Getting at the Innards of an XML Document
We will briefly look at some of the XML Dom objects. In order to show how the code accesses the various parts of a document, consider the following well formed XML document WebClass.xml which is located on my hard drive at:
C:/Documents and settings/computer user/Desktop/todo/Ajax/WebClass.xml.
XML document probed
<?xml version="1.0" encoding="ISO-8859-1" ?><wclass> <!-- My students who attended my web programming class --> <student id="1"> <name>Linda Jones</name> <legacySkill>Access, VB5.0</legacySkill> </student> <student id="2"> <name>Adam Davidson</name> <legacySkill>Cobol, MainFrame</legacySkill> </student> <student id="3"> <name>Charles Boyer</name> <legacySkill>HTML, Photoshop</legacySkill> </student> <student id="4"> <name>Charles Mann</name> <legacySkill>Cobol, MainFrame</legacySkill> </student> </wclass>
Slicing the XML document
First things first: we need to instantiate an XMLDocument object. This is done easily by running the following code as shown:
<SCRIPT LANGUAGE="JavaScript">var xml_doc = new ActiveXObject("Microsoft.XMLDOM");
xml_doc.async = false;
xml_doc.load ("C:/Documents and settings/computer user/Desktop/todo/Ajax/WebClass.xml");
</SCRIPT>
After this step we will look at a number of properities that can be accessed using code.
First Child and Last Child Properties
These properties can be accessed if you insert the following lines after loading the document as shown above but before </Script>,
document.write ("This documents first child is a " +xml_doc.documentElement.firstChild.nodeName);
document.write("<br>");
document.write ("This document's last child= " +
xml_doc.documentElement.lastChild.nodeName);
and if you display it in a browser you would see the following:
This documents first child is a #commentThis document's last child= student
Roaming through XMLDOM: An AJAX Prerequisite – Continuing the Dissection
Document Element Properties
The Document element has a child nodes collection you can access by iterating through the index. You can also access the attributes, which consist of a name and a value as shown here. The following code should be inserted after creating the XMLDOC object.
document.write("<br>"+xdoc.documentElement.childNodes.item(0).text +"<br>");document.write("<br>"+xdoc.documentElement.childNodes.item(1).
attributes[0].name +"<br>");
document.write("<br>"+xdoc.documentElement.childNodes.item(1).
attributes[0].value +"<br>");
When the above snippet is run you would see the following.
My students who took web programming class with meid 1
Nodes Collection
In the document we are looking at, we have a number of student nodes. We can get a reference to the student nodes using the getElementsByTagName method. From this we can find the number of such student nodes. Once we know how many there are (nodes.length), we can iterate through the collection and find the individuals as shown in the following code.
var nodes=xdoc.getElementsByTagName("student");document.write("<br>Number of student nodes is "+nodes.length+"<br>");
for (i=0; i< nodes.length; i++)
{document.write("<br>"+nodes.item(i).text+"<br>");}
If you were to include the code in the script after instantiating the xdoc object you would see the following:
Number of student nodes is 4Linda Jones Access, VB5.0 Adam Davidson Cobol, MainFrame Charles Boyer HTML, Photoshop Charles Mann Cobol, MainFrame
Once you locate an element’s child node through its collection, as in the declaration of Elem in the next snippet, you could determine its name, type and value by calling respectively the nodeName, nodeType and nodeValue properties as shown.
var Elem=xdoc.documentElement.childNodes.item(2);document.write("<br>"+Elem.nodeName+"<br>");
document.write("<br>"+Elem.nodeType+"<br>");
document.write("<br>"+Elem.nodeValue+"<br>");
We have located the second child and its properties by running the above script as shown.
student1 null
As mentioned earlier, in addition to the tree representation (tree metaphor) there is also the family representation (somewhat seniority based). These are usually parent, sibling, previous sibling, next sibling, and so on. The next few lines of code will give you an idea how we may access them. We will be starting with the second student whose id=2.
//second student is referenced.var midElem = xdoc.documentElement.childNodes.item(2);
document.write("<br>"+midElem.attributes[0].value+"<br>");
document.write("<br>"+midElem.previousSibling.text+"<br>");
document.write("<br>"+midElem.nextSibling.text+"<br>");
document.write("<br>"+midElem.parentNode.nodeName+"<br>");
document.write("<br>"+midElem.parentNode.nodeType+"<br>");
document.write("<br>"+midElem.parentNode.nodeValue+"<br>");
When the above code is inserted after instantiating xdoc as described previously, you would see the following displayed.
2Linda Jones Access, VB5.0 Charles Boyer HTML, Photoshop wclass 1 null
Roaming through XMLDOM: An AJAX Prerequisite – Manipulating the XML Document
(Page 4 of 4 )
The XMLDOC object also has a large number of methods that can be invoked to create, modify, and delete nodes. The document object represents the XML file in its entirety and is also a node in the DOM. It has a large number of properties and many methods. For complete documentation the reader is referred to the W3C site. In addition to W3C there are also Microsoft extensions.
Adding a new student to the web class
Being a node, XMLDOC shares the properties of the nodes as well. A complete usage of the methods is not attempted, but a few examples are shown as related to the XML document we have been considering, the WebClass.xml file. XMLDOC object’s methods will be invoked to add another student to the web class file. This is what a student node looks like. We will add another student according to this scheme.
<student id="1"> <name>Linda Jones</name><legacySkill>Access, VB5.0</legacySkill> </student>
Looking at the node, student, we see that in order to add a new student node we need to add the name and legacySkill child nodes. Also we need to add the attribute Id and give a value to it. Finally we need to add the proper texts for the name and legacySkill nodes.
We assume that the new student will have:
id –> 7
name–>John Doe
legacySkill–>Fortran, SorobanThe code for adding the new student is as follows:
//This creates a new student:
var newElem= xdoc.createElement(”student”);
//This line creates the attribute to the new student:
var newAtt=newElem.setAttribute(”id”,7);
//The next two lines creates elements name and legacySkill
var part1=xdoc.createElement(”name”);
var part2=xdoc.createElement(”legacyskill”);
//The next two lines create the required text for the nodes:
var newText1=xdoc.createTextNode(”John Doe “);
var newText2=xdoc.createTextNode(”Fortran, Soroban”);
//Appending the name and legacySkill to the student node
var part11=newElem.appendChild(part1);
var part21=newElem.appendChild(part2);
//Adding the text content to the name and legacySkill nodes
part11.appendChild(newText1);
part21.appendChild(newText2);
In this fashion you build the tree so that the node is defined according to the blueprint from the XML document. This completes the building of the student node.
Verifying the new student information
In order to verify, we again invoke the XMLDOC’s properties as shown in the early part of this tutorial. In particular we will be using the following code to verify:
//We created newElem and the node’s name is given by: document.write(newElem.nodeName);
//We get the following result for this line:
student
//We have not added the newElem to xdoc and presently the number of //nodes in xdoc are given by:
var totalnodes=xdoc.getElementsByTagName(”student”);
document.write(”<br>”+totalnodes.length);
//the result of this code gives the number of student nodes in xdoc:
4
//We look at the attributes of the new student node:
document.write(”<br>”+ newElem.attributes[0].name);
document.write(”<br>”+ newElem.attributes[0].value);
//We get the following result for the above code snippet:
id
7
//We now grab the text for the entire student node:
document.write(”<br>”+newElem.text);
//This snippet gives the following browser display:
John Doe Fortran, Soroban
//Now we get a reference to the last child of the xdoc before adding the //new student:
var lastnode=xdoc.lastChild;
//This is important. We add the newElem student node to the xdoc:
lastnode.appendChild(newElem);
//We now count the number of nodes in the new document which have //the student nodes:
var totalnodes=xdoc.getElementsByTagName(”student”);
document.write(”<br>”+totalnodes.length);
//The result of this in the browser display is:
5
This verifies that the new student has been added (we started out with 4). The file can be saved using the save (ObjTarget) method of the XMLDOC object.
Summary
Understanding and manipulating the XMLDOM objects is a prerequisite to writing successful code, whether it be for AJAX or for anything to do with XML files especially using Microsoft tools. It is the author’s hope that the surgical presentation of this tutorial in dissecting the XMLDOM object will provide a guide to this understanding. XMLHTTP request is another object belonging to the XMLDOM; it is discussed in a previous tutorial and will be revisited again in discussing the responseXML method of the request object.
AJAX has been exciting many programmers as the latest and greatest thing in web development. This article takes a step-by-step look at the Microsoft way to script for AJAX.
Introduction This tutorial is not about Ajax Telamon from the Iliad who fought Hercules, but the latest and greatest (at least in the opinion of some) thing in web development. Ever since Google charmed the web at large with those AJAX-created Google Maps apps, the number of amount of adherence to AJAX has been growing exponentially. In this tutorial, we look mainly at the Microsoft way of scripting for AJAX. Like my previous tutorials, it’s step by step all the way after a brief introduction.
Web Application EvolutionSince 1992 developers have relentlessly pursued ways to make the web more appealing, more productive and more interactive. The plain text html at birth soon saw embedded images followed by appealing colors conferred by cascading style sheets. But it was left for JavaScript to bring user interaction, and thus began the heydays of scripting languages.
However, there were two major browsers sharing the web space, and two scripts to serve the user (the third, ECMA came later). This was not enough, and DHTML was born – not a new language, but a good mix of html, CSS, and JavaScript that made user interaction extremely satisfying.
AJAX came along to make the interaction more responsive without needing to refresh the page. AJAX is an acronym, and the credit for coining this name goes to Adaptive Path’s S. S. Garret. However, Microsoft appears to be the progenitor, with its flagship ActiveX Technology making this possible. Now it is a mélange of XHTML, CSS, JavaScript, XmlHttpRequest, and XML, and its DHTML déjà vu all over.
Step by Step to AJAX – Ajax’s Model of Interaction
The figure shown next is derived from Garret’s article on Ajax, redrawn to render it horizontally. The figure shows both the prior art (classic interaction) as well as the AJAX embodiment. In the classic version, the client sends an HTTP(S) request to the server, the server checks to see whether any server side processing is to be carried out, processes the server related instructions and sends back the html with CSS, if it is defined. There is a synchronicity between client request and server response.
In the AJAX model, the client sends a JavaScript to AJAX, and AJAX sends the HTTP(S) request to the web server which can also serve XML. The web/XML server sends back the result in the form of XML, and AJAX relies on the Document Object Model (DOM: blue print of web document) to transmit the result in XHTML and CSS. It does so by reading the returned XML’s node tree using DOM. The interaction could be synchronous or asynchronous.
Step by Step to AJAX – XmlHttpRequest
One of the key objects needed for working with AJAX is the XMLHttpRequest Object. This object does two things: it handles the httpRequest, and then it processes the XML response. This was implemented in Internet Explorer 5 for Windows as an ActiveX Object. This has been implemented by Mozilla 1.0, Apple and others in compatible forms. The W3C has similar standards for the DOM Level 3 specification. We will take a brief look at this object before we put it to work. Some of the highlighted methods and properties are later used while fetching a web page to show what values they do return.
Creating the XML HttpRequest objectThis is the easy part, if you need to deal only with Microsoft IE. For Mozilla you would instantiate the object using the following syntax:
var xhr=new XMLHttpRequest();
where as for IE you would use the following:
var xhr=new ActiveXObject(”Microsoft.XMLHTTP”);
Both of these methods create an object which is hidden from the user. Its methods and properties take control of the server interaction. For cross browser applications you need to come up with a branching syntax. This is not so difficult; see for example the cross-browser boiler plate code (Listing 2.0 in the link).
Step by Step to AJAX – XMLHttpRequest Object Methods
This short list of methods is shared by all supported browsers. In this tutorial we shall look at some of the highlighted methods.
While open() and send() are most often used, the others are also useful in some cases. The open() method begins the interaction and takes two mandatory arguments; the “method” used to open, which is either GET or POST, and the URL to which the request is made.
The GET and POST methods are similar to those used in ASP, as some of you might recognize. The GET is indicated for retrieving read only data while POST is for sending data to the server.
The URL could be complete, or a relative URL.
The third parameter which is optional (default value TRUE being asynchronous) sets the interaction to be synchronous (FALSE) or asynchronous (TRUE). If you choose the synchronous route, the script waits for the response to arrive before acting. The useful mode is to set the interaction to be asynchronous and use the onreadystatechange event (to be discussed) to get at the response.
XMLHttpRequest Object PropertiesThe following read only properties are browser agnostic (all supported browsers understand them).
Step by Step to AJAX – Fetching a page using AJAX
Working with AJAX is quite simple. The code will be explained by explaining the component parts with reference to an html file, AjaxTest.htm. First you need to create an XMLHttpRequest Object along the lines discussed above. Then you make a request, in this case pass an URL as an argument to the getPage() function. Since you are getting back a page you just need to use the GET argument. Since an asynchronous interaction is intended, use TRUE for the third argument in the open() method.
Before the open() method is called the onreadystatechange eventhandler calls the statusCheck() function, where the readyState of the request is verified and the status code is verified to return OK. Both of these functions are in the <Head/> and are evaluated before the rest of the page is loaded. Review the two functions just described for the html page AjaxTest.htm. Some of the properties and methods previously described and highlighted are also returned. The script that follows this paragraph is in the <head/> section of AjaxTest.htm.
You also include code to alert you if, for some reason, the XMLHttpRequest object instantiation failed. It also needs to alert the user if the request failed because the page was unavailable, or the server was not responding, or for any other reason.
If the status is OK and the readyState shows completion, then you get the response, for this example, in the text format. Now you invoke the DOM API to get the element whose ID=”x”, in this case the <div/>, and set its innerHTML property the same as the returned text. If you look at the response produced by the alert(xhr.responseText) you would see the html of the page you requested as shown in the next picture. I particularly like the preference to use the ECMA scripting style.
<SCRIPT LANGUAGE=javascript>
<!--
var xhr = false;
function getPage (url) {
xhr = false;
//this is the Microsoft browser compatible instantiation
xhr = new ActiveXObject("Microsoft.XMLHTTP");
if (!xhr) {
alert ('XMLHttpRequest failed to instantiate');
return false;
}
xhr.onreadystatechange = statusCheck;
xhr.open ('GET', url, true);
xhr.send (null);
}
function statusCheck() {
txt1.value="XmlHttpRequest_Status: " + xhr.status;
txt2.value="XmlHttpRequest_readyState: " + xhr.readyState;
TEXTAREA1.value="XmlHttpRequest_getallResponseHeaders(): " +
xhr.getAllResponseHeaders();
txt5.value="XmlHttpRequest_statusText: " + xhr.statusText
if (xhr.readyState == 4) {
if (xhr.status == 200) {
alert(xhr.responseText);
document.getElementById("x").innerHTML =(xhr.responseText);
} else {
alert ('There was a problem with the request.');
}
}
}
//-->
</SCRIPT>

However, when you use the DOM API, you get the following as shown:

The <body/> of the AjaxTest.htm consists of just the following controls so that the properties, the methods and their values can be displayed. The onclick event starts the process rolling. Of course the <div/> is where the innerHTML property gets written. The html for these controls is as shown:
<P><STRONG><SPAN
style="FONT-SIZE: large; COLOR: red">A</SPAN>synchronous <SPAN
style="FONT-SIZE: large; COLOR: red">J</SPAN>avascript <SPAN
style="FONT-SIZE: large; COLOR: red">A</SPAN>nd <SPAN
style="FONT-SIZE: large; COLOR: red">X</SPAN>ML= <SPAN
style="FONT-SIZE: large; COLOR: blue">AJAX</SPAN></STRONG></P>
<P>
</P>
<P> </P>
<P><INPUT id=button1 type=button
onclick="getPage ('http://xphtek/TestXMLHttp/Greeting.asp')"
value="Get the page by XmlHttpRequest" name=button1></P>
<P><INPUT id=txt1 style="WIDTH: 396px; HEIGHT: 22px" size=51></P>
<P><INPUT id=txt2 style="WIDTH: 395px; HEIGHT: 22px" size=50></P>
<P><TEXTAREA id=TEXTAREA1 style="WIDTH: 393px; HEIGHT: 72px"
name=TEXTAREA1 cols=42></TEXTAREA></P>
<P><INPUT id=txt5 style="WIDTH: 389px; HEIGHT: 22px"
size=50></P>
<div id="x"></div>
As previously mentioned you can reference the URL completely, as in http://xphtek/TestXMLHttp/Greeting.asp or simply by its relative URL Greeting.asp. The Greeting.asp page is a simple page as shown:
<%@ Language=VBScript %>
<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">
<script language="javascript">
document.write (document.lastModified);
</script>
</HEAD>
<BODY>
<%Response.Write("<b>Welcome to programming with AJAX</b>")%><br>
<% =Date()%><br>
Hello<br>
</BODY>
</HTML>
Summary The tutorial has looked at the various properties and methods of this interesting XMLHttpRequest object. It goes without saying that the browser should support JavaScript, and as a developer you should appropriately warn the user to enable JavaScript, if it is not. For Microsoft IE there is the additional requirement of browser support for ActiveX objects.
For recent browsers the older method of using iframes to update parts of a page will be replaced. However iframes may still find favor with much older browsers. In this tutorial we saw most of the properties, but the lastmodified property returned null; the syntax was double checked but to no avail. Although AjaxTest.htm was coded on Visual Interdev 6.0, it is not a requirement; plain Notepad should work as well.

CodePress is web-based source code editor with syntax highlighting written in JavaScript that colors text in real time while it’s being typed in the browser.
Features
You can try some features with the demo.
* Real-time syntax highlighting » just write some code
* Code snippets » on PHP example type “if” and press [tab]
* Auto completion » simple type ” or ( or ‘ or [ or { on any example below (except Plain Text)
* Shortcuts » on PHP example press [ctrl][shift][space]. It’s shortcut to
* Multiple windows » you can add multiple CodePress windows to the same page
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
![]() | ![]() | ![]() | ![]() | ![]() | ![]() |
| By N2H | |||||