Oomoo

August 20, 2008

How Do I ‘Refresh’ Individual Form Fields?

Filed under: ajax, controller, forms, model, rails, view — oomoo @ 11:13 pm

Recently, I wrote about creating a Ruby on Rails app that would create HTML formatted signatures for Outlook and web-based e-mail systems  (see: Outlook and the Double-Spaced Signature ).  This small application was my first chance to do true data-based entry using form fields (most of my prior RoR projects just tapped into existing databases for searching and presentation in a browser).

Well, most of my software development background has focused primarily on creating data-based applications with heavy data-entry and reporting, so I was eager to see if Rails was up to the task.  I used the Scaffolding classes built into Rails 2.1x to initially generate the pages (see, I wanted to call them screens or forms) to do the CRUD work, then I set about understanding how they worked and editing them to look a little better.

I understand the whole “disconnected” “stateless” model of web development, but I never dreamed it would be this dang hard to put a drop-down box and a couple of text fields inside form tags on a page and get them to interact with my database and with each other.  Of course, now that I know how to do it, it seems simple (and Rails is great again, the web makes sense, and all is right with the world).  Of course, what I could have finished in VFP in about 60 seconds took me many frustrating hours with more than one Rails/Ajax book thrown across the room.

For this application, I only have 2 tables (told you it was simple); Locations and Signatures.  The “Locations” table is a lookup table containing information that will be related to multiple “Signatures” (usually one Signature per person).

Locations Table:Locations schema

Signatures Table:Signatures schema

 

 

 

 

 

 

 

 

 

Locations.id  — Relates to —>> Signatures.location_id

Since each Signature relates back to a Location, if the Company changes their logo or web site, no change to the Signature record is needed, you need only regenerate the HTML signature for Outlook.  Okay, enough “database theory 101”.

Here is what I wanted to accomplish:

captured_Image.png Display a page where the user could select their “Location” from a drop-down combo field…

 

 

 

…once selected, the data from the “Location” phone/fax fields would automatically populate the first two phone fields of the “Signature” record. SigAfterLocation

…the phone fields would temporarily “highlight” to indicate to the user that something had changed

…then the fields would go back to their normal color, still containing the updated data from the Location record.

captured_Image.png[4]

 

 

 

 

Finally, the user “Saves” their Signature, and clicks the “Create My Signature” link which generates/downloads the HTML file containing their nicely formatted Outlook Signature.

captured_Image.png[6]

 

Piece-O-Cake!!!  No really, I wrote a complete version of this page using my desktop development tool (VFP) and it took me almost no time to have all the logic working and I even had a browser preview of the finished HTML that updated itself as you updated your signature.   Here is a screenshot of my desktop form written in VFP…

captured_Image.png[29]

 

We need our page to contain:

  • A Form (form_for)
  • A Drop-Down List (collection_select)
  • Some Labels (form.label)
  • Some Fields (form.field)
  • A Submit Button (form.submit)
  • Some way to know when a selection has been made from the drop-down list
  • Some way of updating the page based on the drop-down selection

 

    <% form_for @signature do |f| %>
    <div id="cbo_location_id", style="height: 31px">
        <%= f.label :choose_location, "Choose Your Location:", 
                    :style => "top: 5px; left: 3px; position: relative; color: white; font-weight: bolder;" %>

        <%= collection_select("signature", "location_id", @locations, "id", "loc_name", 
                              options ={:prompt => "...Select A Location", 
                              :include_blank => false}, 
                              html_options={:style => "top: 7px; left: 15px; position: relative;"}) %>
                              
        <%= observe_field("signature_location_id", 
                          :url => { :action => "location_lookup" }, 
                          :with => "'id=' + value") %>
    </div>
    
    ...other form fields here...

        <table width="350" height="150" cellpadding="0" cellspacing="0">
           <tbody> <tr> <th align="center"> <%= f.label :phone_desc, "Desc.", :style => "color: white;" %> </th>
                        <th align="center"> <%= f.label :phone_num, "Number", :style => "color: white;" %> </th></tr>
                   <tr> <td align="center"> <%= f.text_field :sig_p1_desc, :size => "10", :style => "" %>  </td>
                        <td align="center"> <%= f.text_field :sig_p1_num, :size => "20", :style => "" %>   </td></tr>
                   <tr> <td align="center"> <%= f.text_field :sig_p2_desc, :size => "10", :style => "" %>  </td>
                        <td align="center"> <%= f.text_field :sig_p2_num, :size => "20", :style => "" %>   </td></tr>
                   <tr> <td align="center"> <%= f.text_field :sig_p3_desc, :size => "10", :style => "" %>  </td>
                        <td align="center"> <%= f.text_field :sig_p3_num, :size => "20", :style => "" %>   </td></tr>
                   <tr> <td align="center"> <%= f.text_field :sig_p4_desc, :size => "10", :style => "" %>  </td>
                        <td align="center"> <%= f.text_field :sig_p4_num, :size => "20", :style => "" %>   </td></tr>
             </tbody>
          </table>

    <div style="height: 35px"> <p> <%= f.submit "&nbsp;Save&nbsp;" %>  </p>  </div>
  <% end %>

 

We need our form to:

  • Know that a selection has been made from the drop-down list:

       ( \app\views\signatures\edit.html.erb )

    <%= observe_field("signature_location_id",                           
                       :url => { :action => "location_lookup" },
                       :with => "'id=' + value") %>
  • Retrieve the chosen “Location” data:

      ( \app\controllers\signatures_controller.rb )

    class SignaturesController < ApplicationController
    
      def location_lookup
        @location = Location.find(params[:id])
      end
      ...other controller methods...
    end
  • Update/refresh the fields on the page that have changed, and highlight them: 
      ( \app\views\signatures\location_lookup.js.rjs )

    page[:signature_sig_p1_desc].value = 'p.' page[:signature_sig_p1_num].value = @location.loc_phone.strip page[:signature_sig_p2_desc].value = 'f.' page[:signature_sig_p2_num].value = @location.loc_fax.strip page.visual_effect :highlight, :signature_sig_p1_desc, :duration => 2 page.visual_effect :highlight, :signature_sig_p1_num, :duration => 2 page.visual_effect :highlight, :signature_sig_p2_desc, :duration => 2 page.visual_effect :highlight, :signature_sig_p2_num, :duration => 2

 

So, what is happening here?

  1. My drop-down list is:

        <%= collection_select…  %>
  2. When it’s value is changed, my “field observer” gets fired:

       <%= observe_field…  %>
  3. It’s "action" is defined as:

        :url => { :action => "location_lookup" }  
    which means it is going to call the controller action/method named "location_lookup"
  4. As a parameter, it is going to pass the "id" of the Location chosen:

       :with => "’id=’ + value"
  5. The "location_lookup" action/method in the controller "finds" the passed in Location ID:

       @location = Location.find(params[:id])
  6. Then (by magic) rails renders the contents of the RJS file.

    Actually, Rails will always look for an RJS file to exist that is named the same as the controller action/method.
  7. The RJS file will first update the field values with the contents of the Location data:

        page[:signature_sig_p1_num].value = @location.loc_phone
  8. Then, the RJS file will temporarily "highlight" the changed fields:

       page.visual_effect :highlight, :signature_sig_p1_num, :duration => 2

 

*Note – we didn’t need to use an RJS file, we could have achieved the same thing by putting the RJS code directly in the "location_lookup" controller action/method:

def location_lookup
  @location = Location.find(params[:id])

  render :update do |page|
    page[:signature_sig_p1_desc].value = 'p.'
    page[:signature_sig_p1_num].value = @location.loc_phone.strip
    page[:signature_sig_p2_desc].value = 'f.'
    page[:signature_sig_p2_num].value = @location.loc_fax.strip

    page.visual_effect :highlight, :signature_sig_p1_desc, :duration => 2
    page.visual_effect :highlight, :signature_sig_p1_num, :duration => 2
    page.visual_effect :highlight, :signature_sig_p2_desc, :duration => 2
    page.visual_effect :highlight, :signature_sig_p2_num, :duration => 2    
  end 
end

I just prefer to use the RJS file to keep my controller clean of view-specific things, and if you just think that the RJS is taking the place of the View (html.erb) that would normally be rendered after a controller action/method, then it makes sense.

*Note: "page" is an automatic reference to the existing web page object (DOM) that Rails is exposing so we can monkey around with it.  Now, isn’t that nice!

Since RJS is basically just "Railsified" javascript (AJAX), you can do just about anything to the existing web page.  You can add things, hide things, change all the colors, change the words, enable/disable things, and even insert calls to new javascript functions (like links to services on other web sites).

 

In review, the process involved really isn’t that different from what I had to code in my desktop VFP application.  The biggest difference was knowing what form elements to use (and their syntax) then just understanding that the full logic is done in pieces that are spread out over several files (view, controller, RJS).  In the end, the secret sauce for updating my field display is AJAX, simplified by the Rails helpers.

Happy Field Refreshing!!  –Oomoo

 

—UPDATE ––

Do you want to DEMO this application in your browser right now?

Just go here:  Outlook Signature Application

This web-based application is a full-featured signature generator for Outlook.  You can use it to create your signature or just use it as an educational tool.  This application was completely written at (and is hosted on) Heroku!!

(hint:  If you enter your “Location” and “Signature” information, be sure to delete it before leaving the site)

In fact, you can download the full source code here:  Download Source Code

Advertisements

1 Comment »

  1. […] See this post: Application Notes […]

    Pingback by Outlook and the Double-Spaced Signature « Oomoo — September 3, 2008 @ 10:20 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: