Forms

Lecture Notes for CS 142
Spring 2012
John Ousterhout

  • Goals for forms:
    • Display information in an editable form
    • Upload changes to the server
  • Form:
    • Collection of elements, each of which has
      • Name (used by application, often invisible to user)
      • Value
      • User interface
    • Different types of elements provide different ways for the user to edit the value (text entry, checkbox, menu, etc.)

HTML Form Support

  • Simple form example:
    <form action="/product/update" method="post">
      Product: <input type="text" name="product"/><br />
      Price: <input type="text" name="price" value="49.95"/><br />
      <input type="submit" value="Submit"/>
    </form>
    
    • <form> element: overall container for form elements
      • action specifies a URL to invoke when the form is submitted.
      • method attribute indicates which HTTP method to use when communicating with the server; defaults to GET but should almost always be POST (GET limits size of posted data)
      • Arbitrary HTML OK inside <form> (e.g., decorations)
      • Can have more than one form in a page
    • <input> elements: controls for entering data.
      • type attribute specifies which of several controls to use. text is a simple one-line text entry.
      • name attribute: used to identify this particular value when posting form to server.
      • value attribute specifies initial value for this element.
    • <input type="submit"> creates a button for submitting the form.
      • value attribute specifies text to display in button.
  • When the submit button is clicked:
    • HTTP POST request is made to the URL given by the action attribute of the <form> element.
    • Body of the request contains name-value pairs from the <input> elements, URL-encoded (looks like query data in a URL).
    • Result is an HTML page that replaces the form page.
  • On the server side:
    • Like any other HTTP request.
    • Rails makes the form data available through the params hash.
  • Other form elements: see formElements.html for examples.

Issues With Forms

  • Problem #1: page flow
    • Consider an order entry system:
      • One or more pages of forms
      • Final form to confirm order
      • "Thank you for your order" page
    • Use Back button to return to the thank-you page
  • Solution:
    • One URL displays form
    • POST goes to a second URL
    • Redirect after POST:
      • POST page doesn't generate HTML
      • After redirection, POST URL disappears from browser history list
  • Divide URLs:
    • Those that display information (including forms) (GET method)
    • Those that invoke modifications (POST method)
    • Don't mix these two
  • Problem #2: error handling
    • Invalid form data detected by server during POST
    • Desired effect:
      • Redisplay the form page
      • Display error messages about the problems (ideally, display messages next to the offending form elements)
      • Retain all of the data that the user entered in the form

Rails form support

  • Ties in nicely to the ORM system
    • One model object holds one database record
    • Form used to edit part or all of a record
    • Form helpers: methods to generate HTML elements for forms: you don't have to write HTML directly.
    • Validation: mechanism for detecting errors in input data.
  • Form helpers example (student record):
    • Controller:
      @student = Student.find(params[:id])
      
    • Simple view:
      <%= form_for(@student, :url => {:action => :modify, :id =>@student.id}) do |form| %>
        <%= form.text_field(:name) %>
        <%= form.text_field(:birth) %>
        <%= form.text_field(:gpa) %>
        <%= form.text_field(:grad) %>
        <%= form.submit "Modify Student" %>
      <% end %>
      
    • @student argument to form_for: object whose contents are to be displayed in the form
    • :url argument to form_for: URL where form will get posted
    • form.text_field: returns an HTML input element of type text, provides initial value from corresponding attribute of @student:
      <input id="student_name" name="student[name]" size="30"
              type="text" value="Wendy Wilson" />
      
    • Can also mix other templating stuff in with form elements to control formatting and provide other information:
        <%= form_for(:student, :url => {:action => :modify, :id => @student.id}) do |form| %>
          <table class="form">
            <tr>
              <td class="label"><%= form.label(:name, "Name:")%><td>
              <td><%= form.text_field(:name) %><td>
            </tr>
            <tr>
              <td class="label"><%= form.label(:birth, "Date of birth:")%><td>
              <td><%= form.text_field(:birth) %><td>
            </tr>
            <tr>
              <td class="label"><%= form.label(:gpa, "Grade-point average:")%><td>
              <td><%= form.text_field(:gpa) %><td>
            </tr>
            <tr>
              <td class="label"><%= form.label(:grad, "Graduation year:")%><td>
              <td><%= form.text_field(:grad) %><td>
            </tr>
          <table>
          <%= submit_tag "Modify Student" %>
        <% end %>
      
  • Handling posts:
    def modify
      @student = Student.find(params[:id])
      if @student.update_attributes(params[:student]) then
        redirect_to(:action => :show)
      else
        render(:action => :edit)
      end
    end
    
    • Form data automatically available in params: nested hash named after the form.
    • update_attributes copies values from the form data into the model object and saves the model object.
  • Error handling in Rails forms:
    • Validation:
      class Student < ActiveRecord::Base
        validates_format_of :birth, :with => /\d\d\d\d-\d\d-\d\d/,
            :message => "must have format YYYY-MM-DD"
      
        def validate_gpa
          if (gpa < 0) || (gpa > 4.0) then
            errors.add(:gpa, "must be between 0.0 and 4.0")
          end
        end
      
        validate :validate_gpa
      end
      
      • Validation methods get invoked whenever the object is saved.
      • Several built-in validators, such as validates_format_of.
      • Can also define custom validators, such as validate_gpa.
      • errors.add saves an error message related to a particular attribute of the object.
      • If there is a validation error, methods such as save and update_attributes abort and return false.
    • After a validation error, the action method explicitly re-renders the form.
    • In the form view, extract and render all of the error messages for the object:
      <% @student.errors.full_messages.each do |msg| %>
        <p><%= msg %></p>
      <% end %>
      
    • Form helpers:
      • form.text_field and similar methods add an extra <div class="field_with_errors"> around any form elements and labels for which there are error messages: use CSS to display differently.

File uploads with Rails

  • If you use <input type="file">, then you must request a different protocol for posting data:
    <form method="POST" enctype="multipart/form-data">
    
  • In Rails, add an extra argument to the form_for method:
    form_for(... :url => {...}, :html => {:multipart => true})
    
  • Form helper to generate form element:
    form.file_field :image
    
  • Rails knows how to handle this format (some frameworks don't):
    • Normal form data available in the normal way
    • Siphons off uploaded file to a file on disk; the params value for this form element is an object with lots of methods.
  • read method: returns contents of uploaded file.
  • original_filename method: returns file name as selected in the browser.