Drop-down menu from a belongs_to association

I'm trying to enter a client (from clients table (model), client_id)
into a form for appointments. The client_id is supposed to be entered
into the appointments table.

If I use
  <div class="field">
    <%= f.label :client_id %><br />
    <%= select ("post", "client_id", Client.all.collect {|client|
[client.name,client.id]}, :prompt => "Select client" ) %>
  </div>

I get a drop down menu with clients, but when I submit it, it tells me
that it's blank. so I add an "f." to select, like this

  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.select ("post", "client_id", Client.all.collect {|client|
[client.name,client.id]}, :prompt => "Select client" ) %>
  </div>

but when i submit the form it gives me this error
undefined method `merge' for [["Angelina Jolie", 1], ["Barney Stenson",
3], ["Ted Mosby", 2]]:Array

Whyyy?

Leonel *-* wrote:

I'm trying to enter a client (from clients table (model), client_id)
into a form for appointments. The client_id is supposed to be entered
into the appointments table.

If I use
  <div class="field">
    <%= f.label :client_id %><br />
    <%= select ("post", "client_id", Client.all.collect {|client|
[client.name,client.id]}, :prompt => "Select client" ) %>
  </div>

I get a drop down menu with clients,

...but never, ever do database access in the view! That belongs in the
controller.

but when I submit it, it tells me
that it's blank. so I add an "f." to select, like this

  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.select ("post", "client_id", Client.all.collect {|client|
[client.name,client.id]}, :prompt => "Select client" ) %>
  </div>

That is the correct syntax for the f.select call, but may not be
supplying the correct values. What does the generated HTML from that
select look like? Or do you get the error before it even gets to that
point?

This is a case where collection_select will make your life easier.

Best,

Marnen Laibow-Koser wrote:
[...]

so I add an "f." to select, like this

  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.select ("post", "client_id", Client.all.collect {|client|
[client.name,client.id]}, :prompt => "Select client" ) %>
  </div>

That is the correct syntax for the f.select call,

Actually, it isn't. When you're using form helpers inside a FormBuilder
(as in this case), remove the first argument (in this case "post").
Since you're not doing that, you're passing the arguments in funny
places, and FormOptionsHelper is getting confused.

Also, you don't need the parentheses.

Best,

...but never, ever do database access in the view! That belongs in the
controller.

When you're using form helpers inside a FormBuilder
(as in this case), remove the first argument (in this case "post").
Since you're not doing that, you're passing the arguments in funny
places, and FormOptionsHelper is getting confused.

Also, you don't need the parentheses.

You guys are right. So I tried moving it to the controller and used
collection_select and I get this error
undefined method `collection_select'

APPOINTMENTS_CONTROLLER (I use a before_filter at the top for :new and
:edit)

  private
    def load_clients
      @clients = collection_select :client, :client_id
    end

FORM PARTIAL
  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.select ("client_id", @clients, :prompt => "Select") %>
  </div>

Ok seems like I'm almost there...

CONTROLLER
  private
    def load_clients
      @clients = Client.find(:all)
    end

FORM PARTIAL
  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.select ("client_id", @clients.map {|u| [u.name, u.id]},
:prompt => "Select") %>
  </div>

The problem is, when I submit the form I get error...

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.map

Leonel *-* wrote:

Ok seems like I'm almost there...

CONTROLLER
  private
    def load_clients
      @clients = Client.find(:all)
    end

FORM PARTIAL
  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.select ("client_id", @clients.map {|u| [u.name, u.id]},
:prompt => "Select") %>
  </div>

The problem is, when I submit the form I get error...

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.map

And you're calling map on @clients. So probably @clients is nil.
Let's see the whole controller.

Again, though, since you're dealing with ActiveRecord objects, you
probably want to make your life easier by using collection_select.

Best,

Oh and I did removed the unnecessary parenthesis in the select statement

  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.select :client_id, @clients.map {|u| [u.name, u.id]}, :prompt
=> "Select" %>
  </div>

Again, though, since you're dealing with ActiveRecord objects, you
probably want to make your life easier by using collection_select.

I'll try it with collection_select now, it's supposed to be at the form
partial, right? But shouldn't the collection_select logic be placed in
the controller?

THIS IS MY CURRENT CONTROLLER

class AppointmentsController < ApplicationController
  before_filter :load_clients, :only => [ :new, :edit ]

  # GET /appointments
  # GET /appointments.xml
  def index
    @appointments = Appointment.all

    respond_to do |format|
      format.html # index.html.erb
      format.xml { render :xml => @appointments }
    end
  end

  # GET /appointments/1
  # GET /appointments/1.xml
  def show
    @appointment = Appointment.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml { render :xml => @appointment }
    end
  end

  # GET /appointments/new
  # GET /appointments/new.xml
  def new
    @appointment = Appointment.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml { render :xml => @appointment }
    end
  end

  # GET /appointments/1/edit
  def edit
    @appointment = Appointment.find(params[:id])
  end

  # POST /appointments
  # POST /appointments.xml
  def create
    @appointment = Appointment.new(params[:appointment])

    respond_to do |format|
      if @appointment.save
        format.html { redirect_to(@appointment, :notice => 'Appointment
was successfully created.') }
        format.xml { render :xml => @appointment, :status => :created,
:location => @appointment }
      else
        format.html { render :action => "new" }
        format.xml { render :xml => @appointment.errors, :status =>
:unprocessable_entity }
      end
    end
  end

  # PUT /appointments/1
  # PUT /appointments/1.xml
  def update
    @appointment = Appointment.find(params[:id])

    respond_to do |format|
      if @appointment.update_attributes(params[:appointment])
        format.html { redirect_to(@appointment, :notice => 'Appointment
was successfully updated.') }
        format.xml { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml { render :xml => @appointment.errors, :status =>
:unprocessable_entity }
      end
    end
  end

  # DELETE /appointments/1
  # DELETE /appointments/1.xml
  def destroy
    @appointment = Appointment.find(params[:id])
    @appointment.destroy

    respond_to do |format|
      format.html { redirect_to(appointments_url) }
      format.xml { head :ok }
    end
  end

  private
    def load_clients
      # @clients = collection_select :client, :client_id
      @clients = Client.find(:all)
    end

end

Now I have this at the form partial

FORM PARTIAL
  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.collection_select :client, :client_id, @clients, :id, :name %>
  </div>

and I get this error...

undefined method `merge' for "name":String

Remember I have a CLIENTS table and an APPOINTMENTS table. The purpose
of this form is to schedule an appointment, so I'm trying to create a
drop-down menu with clients. Once an appointment has been scheduled, the
id from the clients table, should be inserted at the client_id at the
appointments table.

It's hard to get used to Rails :S but I know it'll be worth it

Again, though, since you're dealing with ActiveRecord objects, you
probably want to make your life easier by using collection_select.

I'll try it with collection_select now, it's supposed to be at the form
partial, right? But shouldn't the collection_select logic be placed in
the controller?

Collection_select is a view helper, so that's where it belongs -- in the View. The collection you pass to it could / should be marshalled in the controller (at least that's how it looks from here). I have seen view code on StackOverflow that included the data request, but I believe that is flat-out wrong. I would try this: Gather up your collection that you wish to present as a select, and save it as a class variable in the controller. Back in your view, pass a symbol of the same name to your collection_select where it asks for the collection in the parameters.

Walter

Leonel *-* wrote:

Now I have this at the form partial

Hmm... give this a whirl

appointments controller:

def new
  @appointment = Appointment.new
  @clients = Client.find(:all)
  respond_to do |format|
    format.html # new.html.erb
    format.xml { render :xml => @appointment }
  end
end

views:

new.html.erb probably just says "render 'form'"?

_form.html.erb

<div class="field">
  <%= f.label :client_id %><br />
  <%= collection_select("appointment", "client_id", @clients, "id",
"name", {:include_blank => true}) %> </div>

Now I have this at the form partial

FORM PARTIAL
<div class="field">
   <%= f.label :client_id %><br />
   <%= f.collection_select :client, :client_id, @clients, :id, :name %>
</div>

and I get this error...

undefined method `merge' for "name":String

Drop the :client from your collection_select, since you are calling it from the form helper (f) you already have passed that as the first element implicitly.

Walter

Walter Davis wrote:

undefined method `merge' for "name":String

Drop the :client from your collection_select, since you are calling it
from the form helper (f) you already have passed that as the first
element implicitly.

Walter

AWESOME!!! Thanks guys, both of your approaches work. I tried one of
them with clients and the other one with services.

APPOINTMENTS_CONTROLLER.RB

class AppointmentsController < ApplicationController
  before_filter :load_clients_and_services, :only => [ :new, :create,
:edit ]
  [...]
  private
    def load_clients_and_services
      @clients = Client.find(:all)
      @services = Service.find(:all)
    end
end

_FORM.HTML.ERB

  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.collection_select :client_id, @clients, :id, :name, :prompt =>
"Select" %>
  </div>
  <div class="field">
    <%= f.label :service_id %><br />
    <%= collection_select "appointment", "service_id", @services, "id",
"name", {:prompt => "Select"} %>
  </div>

AWESOME!!! Thanks guys, both of your approaches work. I tried one of
them with clients and the other one with services.

Since both approaches work...

Is there an approach that is better than the other one?

Or...

Is one approach more "correct" than the other one?

Leonel *-* wrote:
[...]

Now I have a few questions because even though it DOES work, I don't
want to code just because, I would like to understand it.

@railsdog
When I would remove the "f" and submit the form, the presence validator
would tell me I didn't submit anything, and I thought the reason was the
since I didn't include the "f", the controller or model didn't know the
statement was associated. So how come now that I removed the "f", it
does recognize the form submission. I'm guessing it's because of the
first parameter "appointment".

@walterdavis
So is Rails not strict about parameter order? For example, in PHP if
there is a function(parameter1, parameter2) and both are required, they
HAVE to be there or an error will occur. So in Rails, if there is an
implicit parameter you can just drop it? Is there a documentation where
I can find more info about it?

Ruby is quite strict about parameters, just like any other language.
Rails does have a number of functions written to be a bit flexible in
what they can accept, though.

The answer to both your questions is the following, though. The various
form helpers, when called outside of a FormBuilder block, need to know
what object they're dealing with, so they're defined as
select @object, :field, whatever_else
label @object, :field, 'text'
and so on.

Inside a FormBuilder block, though, the object is already known, so they
have been defined differently (by conscious choice on the part of the
Rails core team, not by some magical property of Ruby). So you do
form_for @object do |f|
  f.select :field, whatever_else
  f.label :field, 'text'
end
...leaving off the @object.

Best,

Leonel *-* wrote:

AWESOME!!! Thanks guys, both of your approaches work. I tried one of
them with clients and the other one with services.

Since both approaches work...

Is there an approach that is better than the other one?

Or...

Is one approach more "correct" than the other one?

It's usually nicer to use form_for and the resulting FormBuilder object
when possible.

Best,

Marnen Laibow-Koser wrote:

Leonel *-* wrote:

AWESOME!!! Thanks guys, both of your approaches work. I tried one of
them with clients and the other one with services.

Since both approaches work...

Is there an approach that is better than the other one?

Or...

Is one approach more "correct" than the other one?

It's usually nicer to use form_for and the resulting FormBuilder object
when possible.

I'm sorry, just to make sure I got it, then you mean is better to use

  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.collection_select :client_id, @clients, :id, :name, :prompt =>
"Select" %>
  </div>

than...

  <div class="field">
    <%= f.label :service_id %><br />
    <%= collection_select "appointment", "client_id", @clients, "id",
"name", {:prompt => "Select"} %>
  </div>

right?

Leonel *.* wrote:
[...]

I'm sorry, just to make sure I got it, then you mean is better to use

  <div class="field">
    <%= f.label :client_id %><br />
    <%= f.collection_select :client_id, @clients, :id, :name, :prompt =>
"Select" %>
  </div>

than...

  <div class="field">
    <%= f.label :service_id %><br />
    <%= collection_select "appointment", "client_id", @clients, "id",
"name", {:prompt => "Select"} %>
  </div>

right?

Yes. You've already specified the object once (when creating the
form_for scope) -- don't repeat yourself!

Best,

Walter Davis wrote:
[...]

Collection_select is a view helper, so that's where it belongs -- in
the View.

Right.

The collection you pass to it could / should be marshalled
in the controller (at least that's how it looks from here).

Correct.

I have
seen view code on StackOverflow that included the data request, but I
believe that is flat-out wrong.

You are correct here too. The view should absolutely never touch the
DB. I'm not even sure it should call too many model methods; I want to
start playing around with Mustache to enforce that...

I would try this: Gather up your
collection that you wish to present as a select, and save it as a
class variable in the controller.

No, that should be an @instance variable.

Back in your view, pass a symbol of
the same name to your collection_select where it asks for the
collection in the parameters.

No! Why the hell would you do that? Just give it the name of the
@instance variable.

Walter

Best,