1

I have standard e-commerce models: ProductCategory, Product, Order and OrderItem

In OrderItem model there is product_id column. When user create an order and add a new order item, I want to let him first choose product category (in select_tag) and then load in f.collection_select :product_id only products from this category.

Now my _order_item_fields.html.haml looks like this:

.form-group
  = label_tag 'product_category_id', 'Category'
  = select_tag 'product_category_id', options_for_select(ProductCategory.all, 'id', 'name')

.form-group
  = f.label :product_id, 'Product'
  = f.collection_select :product_id, {# now it's empty}, :id, :name, {prompt: 'Choose category first'}

So, I need to write some JS function to load data, based on selected category. I'm not sure if I need to write def in orders_controller or I can do in orders.coffee as function called on select from product_category_id tag.

Can you show me the right path?

Thanks for any help!

Peter Tretyakov
  • 3,380
  • 6
  • 38
  • 54

1 Answers1

1

I'd write a controller method returning the correct product ids and names depending on the product category and create the right options by js. This makes sense if you have a lot of categories with a lot of different product ids and therefore should scale up well.

Controller

# OrdersController

def product_options
  category_id = params[:category_id]

  render json: {
    products: ProductCategory.find(category_id).products.pluck(:id, :name)
  }
end

This will return json of the following form:

{
  products: [
    [5, 'name of product with id 5'],
    [12, 'name of product with id 12'],
    ...
  ]
}

Routing

You then have to add a route entry for this:

# Routes
get '/route/to/product_options/:category_id', to: 'orders#product_options'

I think you have resources :orders in your routes somewhere, but for brevity I simply created the route like this now - you can amend it for your routes!

Coffee

To get this json, you can use jquery:

# orders.coffee

category_id = 5
$.get("/route/to/product_options/#{category_id}")

To not have a static category_id, simply listen to the change event of your category selector:

load_new_category = (category_id) ->
  $.get("/route/to/product_options/#{category_id}")

$ -> # wait until page is loaded completely 
  $('[name="product_category_id"]').on 'change', ->
    load_new_category $(@).val()

Finally, you have to do something with the returned json to build your options:

set_product_options = (products_json) ->
  options_html = ''

  for product_array in products_json.products
    id = product_array[0]
    name = product_array[1]

    options_html += "<option value='#{id}'>#{name}</option>"

  $('[name="product_id"]').html options_html

load_new_category = (category_id) ->
  # attach it to `set_product_options` with `.done()`
  $.get("/route/to/product_options/#{category_id}").done set_product_options

$ ->
  $('[name="product_category_id"]').on 'change', ->
    load_new_category $(@).val()

Please double check the jquery selectors in case of problems (if rails really generates this names) but this should give you a good start to implement and optimize it to your needs.

(Typed out this from head, so hope there are no typos in it.)

Markus
  • 5,667
  • 4
  • 48
  • 64