Rails ancestry gem + визуализация и выбор категории/подкатегории в новой форме

У меня есть Rails 5.2.2, и я пытаюсь реализовать жемчужину предков.

Итак, что я хочу:

Я хочу, чтобы пользователь создал предложение для автомобильной детали, но в новой форме я хочу иметь возможность выбрать категорию/подкатегории, а затем ввести другие данные, которые у меня есть, а затем отправить форму для создания предложения. Допустим, кто-то хочет добавить на продажу Тормозные колодки. Но сначала нужно выбрать родительские категории. Например

Автомобиль -> Тормоза -> Тормозные колодки

Таким образом, выбрав тормозные колодки, он может создать предложение.

Что я имею:

#category.rb
class Category < ApplicationRecord
  has_ancestry
  has_many :parts
end

-

#part.rb
class Part < ApplicationRecord
  belongs_to :category
end

На данный момент я уже создал его в консоли, чтобы он работал: например.

car = Category.create!(name: "Car")
brakes = Category.create!(name: "Brakes", parent: car)
brake_pads = Category.create!(name: "Brake Pads", parent: brakes)

Я уже также запускаю миграцию rails g migration add_category_to_parts category:references.

И мой взгляд:

#views/parts/new.html.haml
.container
  %h2.center Create offer
  = simple_form_for(@part, html: {class: "form-group" }) do |f|
    .form-group.col-md-8.col-md-offset-2.well
      = f.input :title
      = f.input :make_name, label: "Make"
      = f.input :code, label: 'Part number'
      = f.association :condition, label_method: :name, prompt: "-"
      = f.input :description
      .form-actions
        = f.button :submit, class: "btn btn-primary btn-dark-blue"

Вопрос в том, как я могу отображать категории/подкатегории в моих представлениях-> части -> форма new.html.haml с 3 выпадающими списками (по одному для каждой подкатегории, потому что у меня будет много категорий/подкатегорий), чтобы пользователь мог выбрать их и затем создать предложение?


person Stefanos    schedule 20.12.2018    source источник
comment
Каковы ваши доводы в пользу того, что вы не хотите создавать контроллер категорий? Хотя вы можете вручную устанавливать категории с помощью семян или консоли, категорию следует рассматривать как собственный ресурс (на мой взгляд).   -  person Mark Merritt    schedule 21.12.2018
comment
Привет Марк, большое спасибо за ваш ответ. Я подумал, потому что я создам категории, когда мне не понадобится контроллер для чего-то еще, но я создам его. (Я отредактировал свой вопрос: я хочу отображать категории/подкатегории в представлениях-›parts-›new.html.haml). Если я создам контроллер категорий, знаете ли вы, как отобразить их по частям в новой форме?   -  person Stefanos    schedule 21.12.2018


Ответы (1)


Вам в любом случае нужен CategoriesController, потому что здесь нужны зависимые выпадающие списки. Когда пользователь выбирает категорию в первой, вам нужно сделать запрос AJAX для получения подкатегорий и т. д.

# config/routes.rb
resources :categories, only: [] do
  get :select_item, on: :collection
end

# app/assets/javascripts/application.js
$('#first-dropdown').on('change', function() {
  $.ajax({
    url: 'categories/select_item?category_id=' + $(this).val(),
    dataType: 'json',
    success: function(data) {
      var childElement = $('#second-dropdown');
      childElement.html('');
      data.forEach(function(v) {
        var id = v.id;
        var name = v.name;
        childElement.append('<option value="' + id + '">' + name + '</option>');
      });
    }
  });
});

# app/controllers/categories_controller.rb
Class CategoriesController < ApplicationController
  def select_item
    category = @category.find params[:category_id]
    render json: category.children
  end
end 

# app/inputs/fake_select_input.rb
class FakeSelectInput < SimpleForm::Inputs::CollectionSelectInput
  def input(wrapper_options = nil)
    label_method, value_method = detect_collection_methods

    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options).merge(input_options.slice(:multiple, :include_blank, :disabled, :prompt))

    template.select_tag(
      attribute_name, 
      template.options_from_collection_for_select(collection, value_method, label_method, selected: input_options[:selected], disabled: input_options[:disabled]), 
      merged_input_options
    )
  end
end

# views/parts/new.html.haml
.form-group.col-md-8.col-md-offset-2.well
  = f.input :title
  = f.input :make_name, label: "Make"
  = f.input :code, label: 'Part number'
  = f.association :condition, label_method: :name, prompt: "-"
  = f.input :description
  = f.input :parent_category_id, as: :fake_select, collection: Category.roots, input_html: { id: 'first-dropdown' }
  = f.input category_id, collection: [], input_html: { id: 'second-dropdown' }
  .form-actions
    = f.button :submit, class: "btn btn-primary btn-dark-blue"

Поскольку simple_form полагается на использование модели, вам необходимо создать настраиваемый ввод для полей, не относящихся к модели (родительские категории). Вам нужно сохранить только один category_id в @part, родительские категории всегда можно взять из него. Если вам нужно больше, чем 2 раскрывающихся списка, просто добавьте еще одну функцию или (лучше) измените ее, чтобы вы могли передавать зависимый раскрывающийся список в качестве параметра.

person Vasilisa    schedule 21.12.2018
comment
Василиса спасибо большое за ответ. Теперь проблема в том, что первое раскрывающееся меню содержит все категории/подкатегории, а второе раскрывающееся меню пустое. - person Stefanos; 24.12.2018
comment
@Stefanos, это должно быть Category.roots вместо Category.all для первого выбора. И второй выбор должен быть пустым, пока вы не сделаете выбор в первом, я прав? - person Vasilisa; 24.12.2018
comment
Василиса(почему-то @name не работает), для первого элемента я изменил 'Category.all` на Category.roots, как вы сказали, и он отлично работает и содержит только категории. Что касается второго раскрывающегося списка, вы правы, он должен быть пустым, пока я не выберу категорию в первом, но теперь, когда первый работает именно так, как я хочу с вашим решением, второй по-прежнему остается пустым, даже если я выбираю категорию в первое раскрывающееся меню. Большое спасибо за ваше время и помощь, но я все еще изучаю RoR, потому что мне это очень нравится ;) - person Stefanos; 25.12.2018
comment
@Stefanos, у тебя есть навыки отладки js? Вам нужно проверить с помощью console.log правильность $(this).val() и data с сервера. А что у вас в логах сервера в select_item действии? - person Vasilisa; 25.12.2018
comment
Василиса, я не знаю js, и по этой причине я не могу заставить его работать, я могу заставить его работать идеально с одним раскрывающимся списком, но я хочу что-то лучше, чем ваше решение. Я открываю консоль и у меня есть некоторые ошибки в коде js и конкретно в этой строке ('<option value="' + id '">' + name + '</option>');, но извините, я понятия не имею о js. Если вы скопируете код в js-валидаторе, я думаю, для вас, возможно, вы сразу же решите его. Пишет о синтаксической ошибке - person Stefanos; 25.12.2018
comment
@Stefanos, исправлены опечатки в js - person Vasilisa; 25.12.2018
comment
Спасибо! Теперь у меня нет ошибок от js, но я пытаюсь отправить форму, чтобы увидеть ошибки в журналах моего сервера и говорит, что SELECT "categories".* FROM "categories" WHERE "categories"."ancestry" IS NULL ↳ app/inputs/fake_select_input.rb:3. Если я трачу ваше время, я больше не хочу этого делать, вашей помощи уже достаточно ;) - person Stefanos; 25.12.2018
comment
@Stefanos, мне становится интересно, но удаленная отладка непроста :) Изменяет routes.rb и часть js снова - вам нужно передать правильные параметры в контроллер - person Vasilisa; 25.12.2018
comment
Василиса, все те же ошибки, второй выпадающий список остается пустым после выбора категории и такая же ошибка в журнале сервера. Я знаю, что это нелегко, и вы потратили много времени, чтобы помочь мне, и я ценю это. И вы единственный (так много разработчиков здесь), кто пытался помочь мне в этой очень сложной для меня проблеме, и я уже столько дней застрял на этой проблеме. Но вы решили большую часть моей проблемы, поэтому я приму ваш ответ и попытаюсь каким-то образом решить остальные. Большое спасибо еще раз - person Stefanos; 25.12.2018
comment
Пожалуйста :) Может быть, будет лучше, если вы создадите новый вопрос, если вы не можете решить все проблемы - person Vasilisa; 25.12.2018