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

March 11, 2008

Manually Testing A Model/Controller

Filed under: controller, model, MVC, rails — oomoo @ 1:26 pm

One of the cool things about Rails, is that with almost no work, you can easily test your new Model/Controller combination.

*Newbie Notes

To initially test a new model-controller, put the line:  “scaffold :mymodelname”  in the controller class code (comment out any other existing code).

This allows you to test that everything is hooked up properly using full CRUD scaffolding.

(No new files are created and no coding changes are done, the scaffold is generated on-the-fly). 
 

Start InstantRails (if you are using it)

   …OR…

      Start Apache/IIS

      Start MySQL 

Start the Ruby Server

    start a “ruby console” command line session
    cd myappname
    ruby script/server
    You will see…
    => Booting Mongrel (use ‘script/server webrick’ to force WEBrick)
    ** Starting Rails with development environment… 
     

Browse the View

      http://localhost:3000/myviewname    (ex:  http://localhost:3000/order) 
 

Experiment with the Model

    Start a Ruby Console:
          cmd…  ruby script/console 
    Create a Record
          @myorder = Order.new 
          myorder.quantity = 5
          myorder.price = 5.95
          myorder.desc = ‘Just a test’
          myorder.ordtime = Time.now 
    Save a Record
          @myorder.save 
    Find a Record
          myorder = Order.find :first 
    Delete a record
          myorder.destroy 
     
     

Troubleshooting 

– ActiveRecord::StatementInvalid: Mysql::Error: Column cannot be null: INSERT INTO

    – You have fields in your table that do not allow NULL.  You will need to supply a value for them before you can save.  *scaffold handles all of this for you. 

– Internet Explorer cannot display the webpage

  • You do not have Apache/IIS started.
  • You do not have MySQL started.
  • You do not have the Ruby server started  (See “Start the Server” above). 
    Remember, your “view” webpage is trying to execute ruby scripts that reside back on the server.  Without a server component running, it cannot do anything.
  • You did not enter the address correctly in the browser (mispelled the view name).

Create A New Model/Controller

Filed under: controller, model, MVC, rails — oomoo @ 1:23 pm
These instructions use an example database table named… orders 
Model
    “ruby script/generate model order”     (Note – use the singular name “order”, not “orders”) 
    “ruby script/generate scaffold order”   (This will create BOTH the Model and Controller and will implement the basic Scaffold functionality as your base code) 
    This command also creates the following files:
             app/models/ order.rb
             test/unit/ order_test.rb
             test/fixtures/ orders.yml 

Controller

    “ruby script/generate controller order index”         (Note – use the singular name “order”, not “orders”) 
    creates…
      app/contollers/orders_controller.rb
      app/helpers/orders_controller.rb
      app/views/orders/index.rhtml
      test/functional/orders_controller_test.rb 
     

View

    Automatically created in the Controller step above. 
    To manually create
          create a folder:  app/views/orders/
          create a file:  index.rhtml  (in the new folder) 
     

Test It

  ruby script/server  (Note – Ruby command line, from the “application” folder) 

  http://localhost:3000/orders (Note – “orders” is the name of the “view folder”.  This will display the “index.rhtml” file by default)

Controllers Broken Down

Filed under: classes, controller, rails — oomoo @ 1:13 pm

Controllers are the core of a web request in Rails.

They are made up of one or more actions that are executed on request and then either render a template or redirect to another action.  
 

An action is defined as a public method on the controller, which will automatically be made accessible to the web-server through Rails Routes.

After executing the code in the method, Controller methods (actions), by default, render a page in the app/views directory corresponding to the name of the controller and action.

For example, the index action of the GuestBookController would render the template app/views/guestbook/index.erb by default after populating the @entries instance variable. 

A sample controller could look like this:  
 

    class OrdersController < ApplicationController
      def index
        @order = Order.find :first
      end
     
      def new
        @order = Order.new 
        @order.ordPK = 3
        @order.ordCustPONum =
      end
    end 
     

Session and Request

Just like when using West Wind Web Connection…

The “Session” object and “Request” object (which are created as part of any HTTP “Get” or “Post”) are passed to the Controller. 

This is how the Controller is able to know the “action” (method) that is to be performed.  It extracts it out of the Request object. 
 

Params[]

A “hash” containing the parameters from the “Request” object (or from the URL).

All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method which returns a hash.

For example, an action that was performed through /weblog/list?category=All&limit=5 will include { “category” => “All”, “limit” => 5 } in params.  

It’s also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:  

  <input type=”text” name=”post[name]” value=”david”>

  <input type=”text” name=”post[address]” value=”1 main street”> 

A request stemming from a form holding these inputs will include

{ “post” => { “name” => “david”, “address” => “1 main street” } }.  
 

Session

Sessions allows you to store objects in between requests.

This is useful for objects that are not yet ready to be persisted, such as a Signup object constructed in a multi-paged process,

or objects that don’t change much and are needed all the time, such as a User object for a system that requires login.  

The session should not be used, however, as a cache for objects where it’s likely they could be changed unknowingly.

It’s usually too much work to keep it all synchronized — something databases already excel at.  

You can place objects in the session by using the session method, which accesses a hash:  

  session[:person] = Person.authenticate(user_name, password) 

And retrieved again through the same hash:  

  Hello #{session[:person]} 
 

Response

Each action results in a response object, which holds the headers and document to be sent to the user’s browser.

The actual response object is generated automatically through the use of “renders and redirects” and requires no user intervention.  

An “action” may perform a single “Render” or “Redirect”  
 

Render

Action Controller sends content to the user by using one of five rendering methods.

The most versatile and common is the rendering of a template.

Included in the Action Pack is the “Action View”, which enables rendering of ERb templates. It’s automatically configured.  

The controller passes objects to the view by assigning instance variables:  

  def show

    @post = Post.find(params[:id])

  end 

Which are then automatically available to the View:  

  Title: <%= @post.title %> 
 

You don’t have to rely on the automated rendering.

Especially actions that could result in the rendering of different templates will use the manual rendering methods:  

  def search

     @results = Search.find(params[:query])

     case @results

       when 0 then render :action => “no_results”

       when 1 then render :action => “show”

       when 2..10 then render :action => “show_many”

     end

  end 
 

Redirect

Redirects are used to move from one action to another.

For example, after a create action, which stores a blog entry to a database, we might like to show the user the new entry.

Because we’re following good DRY principles (Don’t Repeat Yourself), we’re going to reuse (and redirect to) a show action that we’ll assume has already been created.  

The code might look like this:  

  def create

    @entry = Entry.new(params[:entry]) 

    if @entry.save

      # The entry was saved correctly, redirect to show

      redirect_to :action => ‘show’, :id => @entry.id

    else

      # things didn’t go so well, do something else

    end

  end 

In this case, after saving our new entry to the database, the user is redirected to the show method which is then executed

To Create A Controller 

(from Ruby cmd prompt)  ruby script/generate controller myname index 

Creates:

  app/contollers/myname_controller.rb

  app/helpers/myname_controller.rb

  app/views/myname/index.rhtml

  test/functional/myname_controller_test.rb 

To Verify:

   (from Ruby command prompt)  ruby script/server

   (browse to) http://localhost:3000/myname 
 
 

See Also: http://betterexplained.com/articles/intermediate-rails-understanding-models-views-and-controllers/
See Also: http://api.rubyonrails.org/classes/ActionController/Base.html 

*Much of this post was transcribed from a Rails screencast.  I am looking for the source to give credit.

MVC Simple Sample

Filed under: controller, model, MVC, view — oomoo @ 12:45 pm

Assumptions: 

    – MySQL Database named “mydata_development” 
    – Table named my_orders 
     
     

Point YML to correct Database: 

    – File Location:  \config\database.yml 
    development:
      adapter: mysql
      database: mydata_development
      username: root
      password:
      host: localhost 
     
     

Model for Table “my_orders”: 

    – File Location:  \app\models\my_order.rb 
    class MyOrder < Activerecord::Base 
    end 
     
     

Controller: 

    – File Location:  \app\controllers\admin\myorders_controller.rb 
    class MyordersController < ApplicationController
      #scaffold :MyOrder – uncomment to have a full on-the-fly web CRUD scaffold.
     
      def index
        @myorder = MyOrder.find :first
      end
     
      def new
        @myorder = MyOrder.new 
        @myorder.ordPK = 3
        @myorder.ordCustPONum =
      end
    end 
     
     

View: 

    – File Location:  \app\views\myorders\index.rhtml 
    <h1>MyOrder#index</h1>
    <p>Find me in app/views/myorders/index.html.erb</p> 
    <%= @myorder.ordPK %>
    <%= @myorder.ordCustPONum %> 
     

To Run: 

Blog at WordPress.com.