[Rails] Display of multi-layered data using select boxes

Background

I am a student of a certain programming school. Many of you may know this. When creating the "Flea Market Clone Site", we implemented the exhibition category with ajax, so I would like to organize it as well as organize my own mind.

In addition, the category setting uses a gem called "ancestry". For this article, it is assumed that the category has been set.

Complete image

category.gif

code

haml:View(items.new.html.haml)


.form-title
  =f.label "category"
  .form-title__required
    %label required
.form-input-select
  = f.select :category, @category_parent_array, {}, {class: 'listing-select-wrapper__box--select', id: 'parent_category'}
.listing-product-detail__category

――This time, only the bottom three lines are important. Others are free.

Controller (items_controller.rb)


  def new
    @category_parent_array = ["---"]
    Category.where(ancestry: nil).each do |parent|
      @category_parent_array << parent.name
    end
  end

  def get_category_children
    @category_children = Category.find_by(name: "#{params[:parent_name]}", ancestry: nil).children
  end

  def get_category_grandchildren
    @category_grandchildren = Category.find("#{params[:child_id]}").children
  end

If you look at the demo, you can see that the categories are displayed in three stages. ・ The first display is new ・ The second display is get_category_children ・ The third display is get_category_grandchildren It has become.

Is the complicated part the new code? You are assigning an array ** that contains only ** "---" to @category_parent_array. In the each statement on the next line, the categories are assigned to the ** array of @category_parent_array set earlier ** one by one. For (ancestry: nil), please check the saved data after setting the category with "ancestry". You can see the meaning.

** The description in the second and third paragraphs is processed by ajax **, so the actions are separated. For params [: parent_name] and params [: child_id], they are the values that fly from JavaScript with ajax. Remember, we'll set it up later.

routes.rb


 resources :items do
    collection do
      get 'get_category_children', defaults: { format: 'json' }
      get 'get_category_grandchildren', defaults: { format: 'json' }
    end
  end

I have set the ** two ajax actions ** that came out on the controller earlier. It is nested in items. To briefly explain the collection, the routing has ** id attached to member **, and the routing does not have ** id attached to collection **. This time, it's a collection ** because you don't have to specify the ** "individual (id)".

The controller basically ** returns the process to the view **, If you set defaults: {format:'json'}, ** returns processing to json file by default **. (You don't have to use respond_to on your controller to sort to a json file.)

ruby:get_category_children.json.jbuilder


json.array! @category_children do |child|
  json.id child.id
  json.name child.name
end

ruby:get_category_grandchildren.json.jbuilder


json.array! @category_grandchildren do |grandchild|
  json.id grandchild.id
  json.name grandchild.name
end

Don't get the file location wrong! !! Store in the same location as the view.

If you perform the action processing of the second and third paragraphs with the controller, you will jump to this json file. (You set it earlier in routes.rb.) You are converting the variables set in the controller to the data for ajax here.

By the way, ** json.array! ** is set when receiving array format data from the controller.

The flow of ajax processing is ** View (category selection)-> JavaScript (fire)-> Controller (processing)-> json.jbuilder (data conversion)-> JavaScript (processing)-> View ** Repeat (I recognize).

Well, finally, JavaScript is here.

JS(items.js)


$(function)(){

  //Select box choices for child and grandchild categories
  function appendOption(category){
    //value="${category.name}"For, category depending on how to take the value in the strong parameter.I think it may be id.
    var html = `<option value="${category.name}" datacategory="${category.id}">${category.name}</option>`;
    return html;
  }

  //Create a view of a child category
  function appendChildrenBox(insertHTML){
    var childSelectHtml = '';
    childSelectHtml = `<div class='listing-select-wrapper__added' id= 'children_wrapper'>
                        <div class='listing-select-wrapper__box'>
                          <select class="listing-select-wrapper__box--select" id="child_category" name="category_id">
                            <option value="---" data-category="---">---</option>
                            ${insertHTML}
                          <select>
                        </div>
                      </div>`;
    $('.listing-product-detail__category').append(childSelectHtml);
  }

 //Create a view of grandchild categories
  function appendGrandchildrenBox(insertHTML){
    var grandchildSelectHtml = '';
    grandchildSelectHtml = `<div class='listing-select-wrapper__added' id= 'grandchildren_wrapper'>
                              <div class='listing-select-wrapper__box'>
                                <select class="listing-select-wrapper__box--select" id="grandchild_category" name="category_id">
                                  <option value="---" data-category="---">---</option>
                                  ${insertHTML}
                                </select>
                              </div>
                            </div>`;
    $('.listing-product-detail__category').append(grandchildSelectHtml);
  }

  //Processing when the parent category is selected (display of child categories)
  $("#parent_category").on('change', function(){
    //Get the value of the selected parent category
    var parentCategory = document.getElementById('parent_category').value;
    //The selected parent category is"---"False if left (default setting), true if changed
    if (parentCategory != "---"){
      $.ajax({
        url: 'get_category_children',
        type: 'GET',
        //This is the value to be sent to the controller.
        data: { parent_name: parentCategory },
        dataType: 'json'
      })
      .done(function(children){
        //First, delete the already displayed child and grandchild categories
        $('#children_wrapper').remove();
        $('#grandchildren_wrapper').remove();
        //Insert the selection of the category select box into the variable called insertHTML. (Variables provided in the very first paragraph)
        var insertHTML = '';
        children.forEach(function(child){
          insertHTML += appendOption(child);
        });
        //Invoking the view of the child category set in the second paragraph
        appendChildrenBox(insertHTML);
      })
      .fail(function(){
        alert('Failed to get the category');
      })
    }else{
      $('#children_wrapper').remove();
      $('#grandchildren_wrapper').remove();
    }
  });
  
  //Processing when a child category is selected (display of grandchild category)
  $('.listing-product-detail__category').on('change', '#child_category', function(){
    var childId = $('#child_category option:selected').data('category');
    if (childId != "---"){
      $.ajax({
        url: 'get_category_grandchildren',
        type: 'GET',
        data: { child_id: childId },
        dataType: 'json'
      })
      .done(function(grandchildren){
        if(grandchildren.length != 0) {
          $('#grandchildren_wrapper').remove();
          $('#size_wrapper').remove();
          $('#brand_wrapper').remove();
          var insertHTML = '';
          grandchildren.forEach(function(grandchild){
            insertHTML += appendOption(grandchild);
          });
          appendGrandchildrenBox(insertHTML);
        }
      })
      .fail(function(){
        alert('Failed to get the category');
      })
    }else{
      $('#grandchildren_wrapper').remove();
      $('#size_wrapper').remove();
      $('#brand_wrapper').remove();
    }
  });
});

Yes. I understand what I mean. I'm Esper. I would like to send you the convenient words ** Do your best **.

This is too long, so ** comment out the code to explain the process **. If it's not easy to understand, I'd like you to check it out.

Thank you for reading the long and sloppy article to the end. LGTM is mandatory for camp students.

The site that was used as a reference

https://qiita.com/ATORA1992/items/bd824f5097caeee09678 @ ATORA1992 (It was a very easy-to-understand article. Thank you!)

Recommended Posts

[Rails] Display of multi-layered data using select boxes
Rails6: Input the initial data of ActionText using seed
[Enum] Let's improve the readability of data by using rails enum
[Ruby on Rails] Introduction of initial data
[Rails] Temporary retention of data by session
[Rails] How to handle data using enum
[Rails] Implementation of search function using gem's ransack
[Order method] Set the order of data in Rails
[Rails 6] Implementation of inquiry function using Action Mailer
[Rails] Implementation of image enlargement function using lightbox2
[Rails] How to use select boxes in Ransack
[Rails] Implementation of batch processing using whenever (gem)
[Rails] Implementation of PV number ranking using impressionist
[Rails] Implementation of image slide show using Bootstrap 3
[Note] Summary of rails login function using devise ①
Display and post multi-layered data by ancestry !! ~ Ajax ~
[Ruby on Rails] Individual display of error messages
[Rails] How to display the weather forecast of the registered address in Japanese using OpenWeatherMap
[Rails] Test of star evaluation function using Raty [Rspec]
[Rails] Addition of columns / Change of data type / column name
[Rails, JS] How to implement asynchronous display of comments
Save data from excel file using Rails gem roo
[Android] Display of input candidates using List Popup Window
I summarized the display format of the JSON response of Rails
[Rails] Implementation of multi-layer category function using ancestry "Preparation"
[Rails] When using ajax, be aware of "CSRF measures".
[Rails] Implementation of multi-layer category function using ancestry "seed"
Construction of data analysis environment using Docker (personal memorandum)
Try using the query attribute of Ruby on Rails