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

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

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

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

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

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

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

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

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
http;//www.marnen.org
[email protected]

Marnen Laibow-Koser wrote:
[…]

so I add an “f.” to select, like this

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

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,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Ok seems like I’m almost there…

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

FORM PARTIAL

<%= f.label :client_id %>
<%= f.select ("client_id", @clients.map {|u| [u.name, u.id]}, :prompt => "Select") %>

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

…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

<%= f.label :client_id %>
<%= f.select ("client_id", @clients, :prompt => "Select") %>

Leonel - wrote:

Ok seems like I’m almost there…

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

FORM PARTIAL

<%= f.label :client_id %>
<%= f.select ("client_id", @clients.map {|u| [u.name, u.id]}, :prompt => "Select") %>

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,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

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

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

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

<%= f.label :client_id %>
<%= f.collection_select :client, :client_id, @clients, :id, :name %>

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

On Oct 7, 2010, at 10:23 AM, Leonel - wrote:

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

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

On Oct 7, 2010, at 10:37 AM, Leonel - 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

Walter D. wrote:

On Oct 7, 2010, at 10:37 AM, Leonel - 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

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

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?

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,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

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
http://www.marnen.org
[email protected]

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:
[…]

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

<%= f.label :client_id %>
<%= f.collection_select :client_id, @clients, :id, :name, :prompt => "Select" %>

than…

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

right?

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

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

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

<%= f.label :client_id %>
<%= f.collection_select :client_id, @clients, :id, :name, :prompt => "Select" %>

than…

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

right?

Walter D. 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,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]