Spree Dropdown boxes for variant option values

我怕爱的太早我们不能终老 提交于 2019-12-03 14:23:28

This is not as easy as it looks since you will be using Spree::OptionValue records instead of variants and at some point you will want to convert back to variants in order to add it to your cart. Combinations might not be possible and/or out of stock so it is highly unpractical to work with option_values.

But nonetheless, you wanted to know how so i set up the following:

@options = Spree::OptionValue.joins(:option_value_variants).where(spree_option_value_variants: { variant_id: @product.variant_ids }).group_by(&:option_type)

This will give you a hash with the keys of the hash being option_types (Size, Color, Length in your case) and the values being arrays of option_values.

You can easily form this into radios like this:

<% @options.each do |option_type, option_values| %>
  <%= content_tag :h2, option_type.presentation %>
  <% option_values.each do |option_value| %>
    <%= radio_button_tag option_type.name, option_value.id %>
    <%= label_tag option_value.presentation %>
  <% end %>
<% end %>

Or for dropdowns:

<% @options.each do |option_type, option_values| %>
  <%= content_tag :h2, option_type.presentation %>
  <%= collection_select :variants, option_type.name, option_values, :id, :presentation %>
<% end %>

And in your controller you would want to find a variant matching those 3 criteria, check if it is in_stock, backorderable or track_inventory? is false and respond with errors or an updated cart :)

I hope this helped

This is what I did to solve this problem. It basically takes the variant_id parameter that was controlled by the radio buttons and turns it into a hidden field controlled by jQuery and AJAX with additional notifications.

I hope this helps someone.

config/routes.rb

# Mount the core routes
Rails.application.routes.draw do 
  mount Spree::Core::Engine, at: '/'
end

# Create new routes
Spree::Core::Engine.routes.draw do 
  post "products/:product_id/get_variant",
       to: "products#toggle_like",
       as: "get_variant",
       constraints: { :format => /(js)/ }
end

app/models/spree/product_decorator.rb

Spree::Product.class_eval do

  # Find the Product's Variant from an array of OptionValue ids
  def find_variant_by_options(array)
    option_values = Spree::OptionValue.where(id: array)
    variants = []
    option_values.each do |option_value|
      variants.push(option_value.variants.ids)
    end
    self.variants.find_by(:id => variants.inject(:&).first)
  end
end

app/controllers/spree/products_controller_decorator.rb

Spree::ProductsController.class_eval do

  # Get the Variant from params[:ids], respond with JavaScript
  def get_variant
    @product = Spree::Product.find_by :slug => params[:product_id]
    @variant = @product.find_variant_by_options(params[:ids].split(','))

    respond_to do |format|
      format.js
    end
  end
end

app/views/spree/products/get_variant.js.erb

// Update hidden field #varient_id's value. 
$("#variant_id").val("<%= @variant.id %>") 
// Update price
$(".price.selling").html("<%= number_to_currency @variant.price %>");
<% if @variant.in_stock? && @variant.available? %> 
// If in stock and available
  $("#add-to-cart-button").prop("disabled", false); // Enable button
  $(".out-of-stock").hide(); // Hide 'out of stock' message
<% else %> 
// Otherwise
  $("#add-to-cart-button").prop("disabled", true); // Disable button
  $(".out-of-stock").show(); // Show 'out of stock' message
<% end %>

app/views/spree/products/_cart_form.html.erb

<%= form_for order, url: populates_orders_path do |f| %>
  ...
  <% if @product.variants_and_option_values(current_currency).any? %>
    <div id="product_variants" class="col-md-6">
      <h3 class="product-section-title"><%= Spree.t(:variants) %></h3>
      <% @product.option_types.each do |option_type| %>
        <%= f.label "option_type_#{option_type.id}", option_type.name %>
        <br>
        <%= f.select "option_type_value_#{option_type.id}",
                     option_type.option_values.all.collect { |v| [ v.name, v.id ] },
                     { include_blank: true },
                     { class: "form-control" } %>
        <br>
      <% end %>
      <%= hidden_field_tag "variant_id", value: "0" %>
      ...
     </div>
  <% else %>
    <%= hidden_field_tag "variant_id", @product.master.id %>
  <% end %>
  ...
    <span class="price selling" 
          itemprop="price" 
          content="<%= @product.price_in(current_currency).amount.to_d %>">
      <%= display_price(@product) %>
    </span>
  ...
    <%= button_tag class: "btn btn-success",
                      id: "add-to-cart-button",
                disabled: @product.variants.any?,
                    type: :submit do %>
      <%= Spree.t(:add_to_cart) %>
    <% end %>
  ...
  <span class="out-of-stock" style="display: none;">
    <%= Spree.(:out_of_stock) %>
  </span>
<% end %>

<script>
  // Call AJAX if all options are selected, otherwise clean up.
  $("#product-variants select").change(function(){
    var option_value_ids = [];
    $("#product-variants select").each(function(){
      option_value_ids.push($(this).val());
    });
    if(option_value_ids.indexOf("") == -1){
      $.ajax({
        url: "<%= get_variant_path(@product) %>?ids=" + option_value_ids.join(','),
        method: "post"
      });
    }else{
      $("#variant_id").val("0");
      $("#add-to-cart-button").prop("disabled", true);
      $(".out-of-stock").hide();
    }
  });
</script>

how do you update/update product images (like the Ruby on Rails Baseball Jersey example) i got these two select and I want to show the blue one

here what I did on te js.erb file (I took the solution up there):

// Update hidden field #varient_id's value. 
$('#variant_id').val('<%= @variant.id %>')
// Render product/_image partial
$('#main-image').html("<% j (render partial: 'image', locals: 
{product: @variant}) %>");
// Update price
$('.price.selling').html('<%= number_to_currency @variant.price %>');
<% if @variant.in_stock? && @variant.available_on %> 
    // If in stock and available
    $('#add-to-cart-button').prop('disabled', false); // Enable button
    $('.out-of-stock').hide(); // Hide 'out of stock' message
<% else %> 
    // Otherwise
    $('#add-to-cart-button').prop('disabled', true); // Disable button
    $('.out-of-stock').show(); // Show 'out of stock' message
<% end %>

but it didn't work, help please

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!