[Rails] Implement star rating (input, save, display)

Introduction

I would like to rank the memo apps that I am developing. This time, I will evaluate it in three stages. Along with that, you will learn about inputting, saving, and displaying evaluations by stars.

reference

jQuery Raty

Add star rating function with Rails

How to use jQuery Raty

Add columns to migration file

Save the evaluation value in the rate column (type: float) of the notes table (memo table) We will assume that the tables and columns have already been created.

The column should be float type because it will store values such as "0.5" and "1.5" when evaluating with half a star.

If you have created it as an integer or string type, you will need to change the type, although the change method differs depending on the database. The article below describes a solution when you're addicted to this type of change swamp.

class CreateNotes < ActiveRecord::Migration[5.2]
  def change
    create_table :notes do |t|
      t.text :title
      t.integer :user_id
      t.integer :category_id
      t.text :explanation
      t.float :rate

      t.timestamps
    end
  end
end
rails db:migrate:reset

Loading javascript, ★ Loading images

While checking the above reference link, load javascript and load the star image.

Method (2 types)

① To use jQuery Raty, download jquery.raty.js from https://github.com/wbotelhos/raty.

Place the downloaded script anywhere on your website.

Place the downloaded jquery.raty.js file in the app / javascripts folder

(2) Load JavaScript in HTML that uses jQuery Raty. Since Raty is a jQuery plugin, you also need a jQuery script.

    <script src="/js/jquery.min.js"></script>
    <script src="/js/jquery.raty.js"></script>

This time, we will implement it by the method of ①.

//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery       #add to
//= require jquery_ujs  #add to
//= require_tree .

Add Gem file

gem 'jquery-rails'
budle install

Model description (linking)

note.rb


class Note < ApplicationRecord
  #Linking with users
  belongs_to :user,optional: true

 #Validation
  validates :title, presence: true
  validates :explanation, presence: true
  
  #Link with category
  belongs_to :category

  validates :rate, presence: true
  validates :rate, numericality: {
    # only_integer: true,
    less_than_or_equal_to: 3,
    greater_than_or_equal_to: 1,
  }

# Numericality = allow the sky
numericality also allows decimals by default. I only want to allow integers in the rate column, so only_integer.
Example
   validates :param3, :numericality => { :less_than_or_equal_to => 3}
   #Is the number 3 or less?
   validates :param3, :numericality => { :greater_than_or_equal_to => 1 }
   #Is the number 1 or more?

end

Controller description

notes_controller.rb


class NotesController < ApplicationController
  before_action :authenticate_user!
  def new
    @note = Note.new
  end

  def create
    # @note = Note.new(note_params)
    @note = current_user.notes.build(note_params)
    @note.save
    redirect_to notes_path
  end

  def index
    # is_Get all records that valid matches
    @categorys = Category.where(is_valid: true)
    @q = Note.all.ransack(params[:q])
    @notes = @q.result(distinct: true)
  end

  def show
    @note = Note.find(params[:id])
  end

  def edit
    @note = Note.find(params[:id])
  end

  def update
    @note = Note.find(params[:id])
    @note.update(note_params)
    redirect_to note_path
  end

  def destroy
    @note = Note.find(params[:id])
    @note.destroy
    redirect_to notes_path
  end

  def search
    @categorys = Category.where(is_valid: true)
    @category = Category.find(params[:id])
    @q = @category.notes.all.ransack(params[:q])
    @notes = @q.result(distinct: true).page(params[:page])
    @title = @category.name
    render 'notes/index'
  end

  private

  def note_params
    params.require(:note).permit(:title, :category_id, :explanation,:user_id,:rate)
  end
end

Here we add a rate column to the note_params method.

Make a memo registration form

_form.html.erb


<%= form_with model:note, local: true do |f| %>
  <div class='form-group'>
    <%= f.label :title%>
    <%= f.text_field :title, class: 'form-control', id: 'note_title' %>
  </div>
  <div class='form-group'>
    <%= f.label :Category%>
    <%= f.collection_select :category_id, Category.all, :id, :name %>
  </div>
  <!--Evaluation-->
  <div class="form-group row" id="star">
    <%= f.label :rate,'importance', class:'col-md-1 col-form-label' %>
    <%= f.hidden_field :rate, id: :review_star %>
  </div>
  <div class='form-group'>
    <div id='editor'>
      <%= f.label :Contents%>
      <%= f.text_area :explanation, rows: 10, class: 'form-control', id: 'note_explanation', "v-model" => "input", name: "note[explanation]" %>
    <h2><i class="fas fa-eye"></i> Preview</h2>
    <div id="preview-field" v-html='input | marked'>
    </div>
    <div ></div>
  </div>
  <%= f.submit 'Registration', class: 'btn btn-success' %>
  </div>
  <% end %>

  <!--Real-time preview-->
  <script type="text/javascript">
    window.onload = function() {
      new Vue({
      el: '#editor',
      data: {
        input: '<%== j @note.explanation %>',
      },
      filters: {
      marked: marked,
      },
      });
    };

   <!--Evaluation-->
    $('#star').raty({
      size     : 36,
      starOff:  '<%= asset_path('star-off.png') %>',
      starOn : '<%= asset_path('star-on.png') %>',
      starHalf: '<%= asset_path('star-half.png') %>',
      scoreName: 'note[rate]',
      half: true,
    });
  </script>

The points are as follows

To be able to input half of # ★ half: true,

Display of rating by star

I want to display ★ in the memo list. The difference from the above "Enter and save ★" is that 1. Display without input, 2. Repeat processing.

_notes_index.html.erb


<div class='row'>
  <table class='table'>
    <thead>
      <tr>
        <th>title</th>
        <th>Category</th>
        <th>importance</th>
      </tr>
    </thead>
    <tbody>
      <% @notes.each do |note| %>
      <% if user_signed_in? && current_user.id == note.user_id %>
      <tr>
        <td>
          <%= link_to note_path(note) do %>
            <%= note.title %>
          <% end %>
        </td>
        <td><%= note.category.name %></td>
        <!--Evaluation-->
     <td>
         <div id="star-rate-<%= note.id %>"></div>
          <script>
            //Note so that id can be unique even in iterative processing_enter id
          $('#star-rate-<%= note.id %>').raty({
            size: 36,
            starOff:  '<%= asset_path('star-off.png') %>',
            starOn : '<%= asset_path('star-on.png') %>',
            starHalf: '<%= asset_path('star-half.png') %>',
            half: true,
            //I can't input just by reading
            readOnly: true,
            score: <%= note.rate %>,
          });
          </script>
        </td>
      </tr>
      <% end %>
      <% end %>
    </tbody>
  </table>
</div>

The points are as follows

Embed review.id so that id can be kept unique even if iterative processing is executed

<div id="note-rate-<%= note.id %>"></div>

Read-only (cannot be entered)

readOnly: true,

Read the input value of the star

score: <%= note.rate %>,

This completes!

Finally

Please note that the explanation may be difficult to understand. Also, if there are any mistakes, I would appreciate it if you could teach me.

Recommended Posts

[Rails] Implement star rating (input, save, display)
[Rails] How to implement star rating
Implement star rating function using Raty in Rails6
[Rails] "Input"-> "Confirmation screen"-> "Save"-> "View"
Implement Rails pagination
[Rails, JS] How to implement asynchronous display of comments
[Rails] Implement search function
Implement Rails account BAN
[Rails] Implement rake task
Implement markdown in Rails
Implementation policy to dynamically save and display Timezone in Rails