[Rails] Implementation of multi-layer category function using ancestry "I tried to make a window with Bootstrap 3"

Target

ezgif.com-video-to-gif.gif

Development environment

・ Ruby: 2.5.7 Rails: 5.2.4 ・ Vagrant: 2.2.7 -VirtualBox: 6.1 ・ OS: macOS Catalina

Premise

The following has been implemented.

Slim introductionIntroduction of Bootstrap3Introduction of Font Awesome -Login function implementationImplementation of posting function -Many-to-many category function implementationMulti-layer category function implementation (preparation)Multi-layer category function implementation (seed) ・ [Multi-layer category function implementation (creation form)] (https://qiita.com/matsubishi5/items/4afb4a4f307023126c66) ・ [Multi-layer category function implementation (editing form)] (https://qiita.com/matsubishi5/items/10e61f314f6c56b8690d)

1. Edit the controller

homes_controller.rb


#Postscript
def category_window
  @children = Category.find(params[:parent_id]).children
end

[Explanation]

(1) Extract the child categories of the categories corresponding to the parameters sent by Ajax communication and assign them to the instance variables.

@children = Category.find(params[:parent_id]).children

2. Create / edit json.jbuilder file

Terminal


$ touch app/views/homes/category_window.json.jbuilder

ruby:category_window.json.jbuilder


json.array! @children do |children|
  json.id children.id
  json.name children.name
end

[Explanation]

(1) Repeat the records extracted by the get_category_children action to create an array.

json.array! @children do |children|

(2) Store each ID and name in the array created by .

json.id children.id
json.name children.name

** ◎ Return value when the mouse is on the parent category (business) **

[
  {
    "id": 2, 
    "name": "Finance"
  },
  {
    "id": 6, 
    "name": "Economy"
  },
  {
    "id": 9, 
    "name": "management"
  },
  {
    "id": 13, 
    "name": "marketing"
  },
]

** ◎ Return value when the mouse is on the child category (finance) **

[
  {
    "id": 3, 
    "name": "stock"
  },
  {
    "id": 4, 
    "name": "exchange"
  },
  {
    "id": 5, 
    "name": "tax"
  },
]

3. Add routing

routes.rb


#Postscript
get 'get_category/new', to: 'homes#category_window', defaults: { format: 'json' }

4. Edit the view

slim:application.html.slim


body
  header
    nav.navbar.navbar-default.navbar-fixed-top
      .container-fluid
        ul.nav.navbar-nav.navbar-right
          li.dropdown role='presentation'
            a.dropdown-toggle data-toggle='dropdown' href='#' role='button' aria-expanded='false'
              i.fas.fa-list-ul
              span
                |Search by category
              span.caret
            ul.dropdown-menu role='menu'
              li role='presentation'
                - Category.where(ancestry: nil).each do |parent|
                  = link_to parent.name, root_path, id: "#{parent.id}", class: 'parent-category'
              br
              li role='presentation' class='children-list'
              br
              li role='presentation' class='grandchildren-list'

[Explanation]

** * I will omit how to write Bootstrap. ** **

(1) The value of ancestry is nil, that is, all the parent categories are extracted and displayed in the pull-down menu.

- Category.where(ancestry: nil).each do |parent|
  = link_to parent.name, root_path, id: "#{parent.id}", class: 'parent-category'

(2) Prepare a place to display the child category.

li role='presentation' class='children-list'

③ Prepare a place to display the grandchild category.

li role='presentation' class='grandchildren-list'

5. Create / edit JavaScript file

Terminal


$ touch app/assets/javascripts/category_window.js

category_window.js


$(function() {
  function buildChildHTML(children) {
    let html = `
      <a class="children-category" id="${children.id}" href="/">
        ${children.name}
      </a>
    `;
    return html;
  }

  $('.parent-category').on('mouseover', function() {
    let id = this.id;
    $('.children-category').remove();
    $('.grandchildren-category').remove();
    $.ajax({
      type: 'GET',
      url: '/get_category/new',
      data: {
        parent_id: id,
      },
      dataType: 'json',
    }).done(function(children) {
      children.forEach(function(child) {
        let html = buildChildHTML(child);
        $('.children-list').append(html);
      });
    });
  });

  function buildGrandChildHTML(children) {
    let html = `
      <a class="grandchildren-category" id="${children.id}" href="/">
        ${children.name}
      </a>
    `;
    return html;
  }

  $(document).on('mouseover', '.children-category', function() {
    let id = this.id;
    $.ajax({
      type: 'GET',
      url: '/get_category/new',
      data: {
        parent_id: id,
      },
      dataType: 'json',
    }).done(function(children) {
      children.forEach(function(child) {
        let html = buildGrandChildHTML(child);
        $('.grandchildren-list').append(html);
      });
      $(document).on('mouseover', '.children-category', function() {
        $('.grandchildren-category').remove();
      });
    });
  });
});

[Explanation]

① Create HTML for the child category.

function buildChildHTML(children) {
  let html = `
    <a class="children-category" id="${children.id}" href="/">
      ${children.name}
    </a>
  `;
  return html;
}

(2) Change the display contents of the child category depending on which parent category the mouse is hovering over.

  $('.parent-category').on('mouseover', function() {
    let id = this.id;
    $('.children-category').remove();
    $('.grandchildren-category').remove();
    $.ajax({
      type: 'GET',
      url: '/get_category/new',
      data: {
        parent_id: id,
      },
      dataType: 'json',
    }).done(function(children) {
      children.forEach(function(child) {
        let html = buildChildHTML(child);
        $('.children-list').append(html);
      });
    });
  });

** ◎ Create an event that fires when the mouse hovers over the parent category. ** **

$('.parent-category').on('mouseover', function() {});

** ◎ Assign the ID sent from category_window.json.jbuilder to the variable. ** **

let id = this.id;

** ◎ For the time being, delete the child category and below. ** **

$('.children-category').remove();
$('.grandchildren-category').remove();

** ◎ Set the variable created earlier in the parameter (parent_id) and execute the category_window action asynchronously. ** **

  $.ajax({
    type: 'GET',
    url: '/get_category/new',
    data: {
      parent_id: id,
    },
    dataType: 'json',
  })

** ◎ If Ajax communication is successful, create HTML for the corresponding child category and display it. ** **

.done(function(children) {
  children.forEach(function(child) {
    var html = buildChildHTML(child);
    $('.children-list').append(html);
  });
});

③ Create HTML for grandchild category.

function buildGrandChildHTML(children) {
  var html = `
    <a class="grandchildren-category" id="${children.id}" href="/">
      ${children.name}
    </a>
  `;
  return html;
}

④ Change the display contents of the grandchild category depending on which child category the mouse is hovering over. (Since it is almost the same as , the explanation is omitted)

$(document).on('mouseover', '.children-category', function() {
  var id = this.id;
  $.ajax({
    type: 'GET',
    url: '/get_category/new',
    data: {
      parent_id: id,
    },
    dataType: 'json',
  }).done(function(children) {
    children.forEach(function(child) {
      var html = buildGrandChildHTML(child);
      $('.grandchildren-list').append(html);
    });
    $(document).on('mouseover', '.children-category', function() {
      $('.grandchildren-category').remove();
    });
  });
});

Caution

If you do not disable turbolinks, the pull-down menu will not work asynchronously, so be sure to disable it.

How to disable turbolinks

Recommended Posts

[Rails] Implementation of multi-layer category function using ancestry "I tried to make a window with Bootstrap 3"
[Rails] Implementation of multi-layer category function using ancestry "Preparation"
[Rails] Implementation of multi-layer category function using ancestry "seed"
[Rails] Implementation of multi-layer category function using ancestry "Creation form"
I tried to make a group function (bulletin board) with Rails
I tried using Hotwire to make Rails 6.1 scaffold a SPA
I tried to make a message function of Rails Tutorial extension (Part 1): Create a model
I tried to make a reply function of Rails Tutorial extension (Part 3): Corrected a misunderstanding of specifications
I tried to make a message function of Rails Tutorial extension (Part 2): Create a screen to display
I tried to implement a function equivalent to Felica Lite with HCE-F of Android
Rails6 I want to make an array of values with a check box
I tried to make a login function in Java
[Rails] Implementation of category function
[Rails] I tried to create a mini app with FullCalendar
I want to make a function with kotlin and java!
I tried to make a client of RESAS-API in Java
[Rails] I tried to implement "Like function" using rails and js
[Rails] gem ancestry category function implementation
I tried to implement Ajax processing of like function in Rails
I want to add a browsing function with ruby on rails
I tried to implement the image preview function with Rails / jQuery
[Unity] I tried to make a native plug-in UniNWPathMonitor using NWPathMonitor
[Rails] Implementation of coupon function (with automatic deletion function using batch processing)
I tried to build a simple application using Dockder + Rails Scaffold
I tried to make a machine learning application with Dash (+ Docker) part2 ~ Basic way of writing Dash ~
Make a login function with Rails anyway
I tried to make a parent class of a value object in Ruby
I tried to make a simple face recognition Android application using OpenCV
[iOS] I tried to make a processing application like Instagram with Swift
[Rails] I will explain the implementation procedure of the follow function using form_with.
I tried to make a Web API that connects to DB with Quarkus
I tried to make a talk application in Java using AI "A3RT"
I tried to make a sample program using the problem of database specialist in Domain Driven Design
[Rails] Implementation of search function using gem's ransack
[Rails 6] Implementation of inquiry function using Action Mailer
I tried to make Basic authentication with Java
[Rails] Implementation of image enlargement function using lightbox2
[Rails] I made a draft function using enum
Let's make a search function with Rails (ransack)
[Rails] Implementation of image slide show using Bootstrap 3
How to make a follow function in Rails
I tried to implement a server using Netty
I tried to break a block with java (1)
I tried to make a machine learning application with Dash (+ Docker) part3 ~ Practice ~
[Rails / JavaScript / Ajax] I tried to create a like function in two ways.
I want to be able to read a file using refile with administrate [rails6]
I tried to make a simple game with Javafx ① "Let's find happiness game" (unfinished)
[Java] I tried to connect using a connection pool with Servlet (tomcat) & MySQL & Java
[Android] I tried to make a material list screen with ListView + Bottom Sheet
I tried to clone a web application full of bugs with Spring Boot
I tried to create a shopping site administrator function / screen with Java and Spring
I tried to make a simple game with Javafx ① "Let's find happiness game" (unfinished version ②)
I tried using the Server Push function of Servlet 4.0
[Rails] Implementation of drag and drop function (with effect)
I want to define a function in Rails Console
I tried to investigate the mechanism of Emscripten by using it with the Sudoku solver
I tried to make a product price comparison tool of Amazon around the world with Java, Amazon Product Advertising API, Currency API (2017/01/29)
I tried to make the sample application into a microservice according to the idea of the book "Microservice Architecture".
I tried to make an introduction to PHP + MySQL with Docker
I tried to introduce Bootstrap 4 to the Rails 6 app [for beginners]
I tried to modernize a Java EE application with OpenShift.