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,