Paulus
Paulus

Reputation: 99

Best practice for custom text_method in collection_select?

I have Rails app with a use case to display prices in a collection_select dropdown box. For instance, names of products and their prices, or clients and amounts owing.

I well know it's bad MVC practice to simply include the relevant helpers to get access to functions like number_to_currency, but the best I can do with a custom method on the Product or Payment model is get a value that looks like $20.2. So, it's readable but sub-optimal in that most users of the system will be expecting twenty dollars and twenty cents to be represented as $20.20.

Does anyone have any suggestions for easily customising the text_method of a collection_select?

Upvotes: 2

Views: 1201

Answers (3)

Paulus
Paulus

Reputation: 99

I ended up building on GoGoCarl's suggestion, but moving the array generation to a helper:

module PaymentsHelper
  def invoices_for_payment_select(invoice)
    @invoices.collect { |i| ["#{i.client.name} - #{number_to_currency(i.outstanding)}", i.id] }
  end
end

This way I can reuse the dropdown box should I need to and keep the view neat.

Upvotes: 0

Matouš Borák
Matouš Borák

Reputation: 15934

The text_method as well as the value_method in collection_select accepts a symbol but also a lambda/proc block which will get the current collection element as a parameter. See the docs here. The lambda is executed in the context of the view so all view helpers should be available inside it.

So to get the model price formatted by a currency formatter you should be able to call collection_select like the following:

<%= collection_select :user, :payment_id, @user.payments, 
                     :id, ->(pmt) { number_to_currency(pmt.amount) } %>

You have not shown your model associations, so this is just an example of a user having many payments for which you want to format the price in the select box text.

Upvotes: 5

GoGoCarl
GoGoCarl

Reputation: 2519

Abandon ship with the collection_select method, and just use select in your view:

<% options = @payments.each { |p| number_to_currency(p.price), p.id } %>
<%= f.select :payment, options %>

Since you're in the view, everything you'd expect is there, both model and helper methods.

I'm making assumptions on your HTML since you didn't give a specific example, but I think the code block above is everything you need to get going. For more details: http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-select

If you are married to collection_select, you could always roll your own number formatter for currency; shouldn't be difficult with pure Ruby. But, the method above will be more powerful in the long run and afford your more options with helpers down the line, and let you use functions consistently across views.

Upvotes: 2

Related Questions