Topic: automatically filling in form values in edits

I want to know how rails handles filling in form fields automatically when using the edit generated by scaffold.  It works great for a single tables fields in the database, but I have a form that inserts data into a link table.  Instead of explaining my problem at a near hypothetical level here is the code:

The form partial:

<!--[form:project]-->
<div class='form-item'>
<label for="project_name">Name</label><br/>
<%= text_field 'project', 'name'  %>
<div class='description'>The Name of the Project</div>
</div>

<div class='form-item'>
<label for="project_abstract">Abstract</label><br/>
<%= text_area 'project', 'abstract', :cols => "30", :rows => "4"  %>
<div class='description'>The basic description and goal of the project</div>
</div>

<div class='form-item'>
<label for="project_lead">Owner</label><br/>
<%=
  @users = User.find(:all, :order => "name")
  collection_select 'resources', 'owner', @users, :id, :name
%>
<div class='description'>The Project Owner</div>
</div>

<div class='form-item'>
<label for="project_resources">Resources</label><br/>
<select name='resourceslist[]' multiple='multiple'>
<%=
  @users = User.find(:all, :order => "name")
  options_from_collection_for_select @users, :id, :name
%>
</select>
<div class='description'>Resources allocated to this project</div>
</div>

<div class='form-item'>
<label for="project_startdate">Start Date</label><br/>
<%= date_select 'project', 'startdate'  %>
<div class='description'>The Date the project is to start</div>
</div>

<div class='form-item'>
<label for="project_duedate">Due Date</label><br/>
<%= date_select 'project', 'duedate'  %>
<div class='description'>The due date of the project</div>
</div>

<div class='form-item'>
<label for="project_udstatus">Status</label><br/>
<%= select( 'project', 'udstatus', %w{ new good okay bad } ) %>
<div class='description'>The current status of the project</div>
</div>

<div class='form-item'>
<label for="project_priority">Priority</label><br/>
<%=
  @projects = Project.find(:all, :order => "priority ASC").map {|p| ["(#{p.priority}) #{p.name}", p.priority]}
  @projects << ["(#{@projects.length + 1})", @projects.length + 1]
  select 'project', 'priority', @projects
%>
<div class='description'></div>
</div>

<!--[eoform:project]-->


The important fields are the select tags with the resources prefix.  Everything else has the values filled in correctly (I'm guessing via the @project passed to it by the controller).

The controller:

class ProjectsController < ApplicationController
  def index
    list
    render :action => 'list'
  end

  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
  verify :method => :post, :only => [ :destroy, :create, :update ],
         :redirect_to => { :action => :list }

  def list
    @project_pages, @projects = paginate :projects, :per_page => 10
  end

  def show
    @project = Project.find(params[:id])
  end

  def new
    @project = Project.new
  end

  def create
    @project = Project.new(params[:project])
    if @project.save
      Project.update_all("priority = priority + 1", "priority >= #{@project.priority} and id != #{@project.id}")
      if create_resources(@project)
        flash[:notice] = 'Project was successfully created.'
        redirect_to :action => 'list'
      end
    else
      render :action => 'new'
    end
  end

  def create_resources(project)
    @lastresource = Resource.find(:first, :conditions => "project_id = #{project.id}", :order => "priority DESC", :limit => "1")
    if @lastresource.nil?
      priority = 1
    else
      priority = @lastresource.priority = 1
    end
    puts "Priority: " + priority.to_s
   
    resourcelist = []
    Resource.create(:user_id => params[:resources][:owner],
                     :project_id => project.id,
             :owner => "1",
             :priority => priority)
    for user_id in params[:resourceslist]
      resourcelist << { :user_id => user_id, :project_id => project.id }
    end
    Resource.create(resourcelist)
  end
 

  def edit
    @project = Project.find(params[:id])
  end

  def update
    @project = Project.find(params[:id])
    if @project.update_attributes(params[:project])
      if params[:oldproject][:prevpriority].to_i - @project.priority.to_i > 0 #Priority becomes lower (Higher Number)
        Project.update_all("priority = priority + 1",
    "priority >= #{@project.priority} and priority < #{params[:oldproject][:prevpriority]} and id != #{@project.id}")
      elsif params[:oldproject][:prevpriority].to_i - @project.priority.to_i < 0 #Priority becomes higher (Lower Number)
        Project.update_all("priority = priority - 1",
    "priority > #{params[:oldproject][:prevpriority]} and priority <= #{@project.priority} and id != #{@project.id}")
      end
      flash[:notice] = 'Project was successfully updated.'
      redirect_to :action => 'show', :id => @project
    else
      render :action => 'edit'
    end
  end

  def update_resources
 
  end

  def destroy
    Project.find(params[:id]).destroy
    redirect_to :action => 'list'
  end
end


And the model to help explain the table relationships:

class Project < ActiveRecord::Base
  has_many :tasks
  has_many :resources
  validates_presence_of :name, :abstract, :startdate, :duedate, :udstatus, :priority
  validates_uniqueness_of :name

  protected
  def validate
  end
end


I'm sorry you'll have to sift through my other kludge, I didn't want to descriminate in case I left out something important in ignorance.

If art interprets our dreams, the computer executes them in the guise of programs.

-SICP (Abelson, Sussman)

Re: automatically filling in form values in edits

OK looking at the documentation, I think you'll need to change from using options_from_collection_for_select, to using options_for_select.  The former allows you to pass in a single selected value, while the latter allows you to pass in multiple selections.

However you might want to try passing an array into your current version first, just in case the documentation omitted the ability to use an array.

The possibly working case would look like  (See line 6 for the only change)

<div class='form-item'>
<label for="project_resources">Resources</label><br/>
<select name='resourceslist[]' multiple='multiple'>
<%=
  @users = User.find(:all, :order => "name")
  options_from_collection_for_select @users, :id, :name, @project.resources # just added a call to pass in the current values as selected
%>
</select>
<div class='description'>Resources allocated to this project</div>
</div>

If you have to switch to the slightly less easy to use options_for select I think the line would look like
  options_for_select @users.collect {|u| [u.name, u.id]}, @project.resources # just added a call to pass in the current values as selected

Last edited by NielsenE (2006-07-08 22:58:31)

My RoR journey  -- thoughts on learning RoR and lessons learned in applying TDD and agile practices.

Re: automatically filling in form values in edits

Thanks for you help pointing me in the right direction.  I got it working using by changing the name of a variable, throwing an extra line into the controller, and changing to options_for_select.  Here's the code:

Partial:

<div class='form-item'>
<label for="project_lead">Owner</label><br/>
<%=
  @users = User.find(:all, :order => "name")
  collection_select 'resources', 'user_id', @users, :id, :name
%>
<div class='description'>The Project Owner</div>
</div>

<div class='form-item'>
<label for="project_resources">Resources</label><br/>
<select name='resourceslist[]' multiple='multiple'>
<%=
  selected_resources = []
  Resource.find(:all, :conditions => "project_id = '#{params[:id]}' AND owner != 1").each { |r| selected_resources<< r.user_id }
  puts selected_resources
  @users = User.find(:all, :order => "name")
  options_for_select @users.collect {|u| [u.name, u.id]}, selected_resources
%>
</select>
<div class='description'>Resources allocated to this project</div>
</div>


And The controller:

  def edit
    @project = Project.find(params[:id])
    @resources = Resources.find(:first, :conditions => "project_id = '#{params[:id]}' AND owner = 1")
  end

If art interprets our dreams, the computer executes them in the guise of programs.

-SICP (Abelson, Sussman)