Using Star Rating jQuery Plugin Raty with Twitter Bootstrap 4 in Rails 5 Apps

Introduction

We will continue working on the Movie database app we developed in the previous article. Style the movie show page.

Libraries Used

  • Twitter Bootstrap 4 for CSS Framework
  • JQuery Raty Plugin for Star Rating

Style Page using Twitter Bootstrap 4

<p id="notice"><%= notice %></p>
<div class="panel panel-default">
  <div class="panel-body">
    <div class="row">
      <div class="col-md-4">
        <%= image_tag @movie.poster.url(:medium) %>

        <div class="table-responsive">
          <table class="table">
            <tbody>
              <tr>
                <td><strong>Title:</strong></td>
                <td><%= @movie.title %></td>
              </tr>
              <tr>
                <td><strong>Description:</strong></td>
                <td><%= @movie.description %></td>
              </tr>
              <tr>
                <td><strong>Movie length:</strong></td>
                <td><%= @movie.duration %></td>
              </tr>
              <tr>
                <td><strong>Director:</strong></td>
                <td><%= @movie.director %></td>
              </tr>
              <tr>
                <td><strong>Rating:</strong></td>
                <td><%= @movie.rating %></td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </div>
</div>
<div class="form-group row">
  <div class="col-sm-10">
    <%= link_to 'Edit', edit_movie_path(@movie), class: 'btn btn-primary' %>
      <%= link_to "Back", movies_path, class: "btn btn-secondary" %>
  </div>
</div>

Add a call to action in the home page by modifying movies/index.html.erb:

<div class='jumbotron'>
  <h1> Your Favorite Movies Reviewed</h1>
  <p>
    Have you ever been to a movie and realized that it was so bad that you kick yourself for not reading the reviews? Kick yourself, no more. 
  </p>
  <p>
    <%= link_to 'Signup To Write a Review', root_path, class: 'btn btn-primary btn-lg' %>
  </p>
</div>

Review a Movie

Create a review resource.

rails g scaffold review rating:integer comment:text

Migrate the database.

rails db:migrate

Style the new review page.

<%= form_for(review) do |f| %>
  <% if review.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(review.errors.count, "error") %> prohibited this review from being saved:</h2>
      <ul>
      <% review.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  <div class="form-group row">
    <%= f.label :rating, class: 'col-sm-2 form-control-label' %>
    <div class="col-sm-6">
      <%= f.number_field :rating %>
    </div>
  </div>
  <div class="form-group row">
    <%= f.label :comment, class: 'col-sm-2 form-control-label' %>
    <div class="col-sm-6">
      <%= f.text_area :comment, class: 'form-control' %>
    <div>
  </div>
  <br/>
  <div class="form-group row">
    <div class="col-sm-10">
      <%= f.submit 'Submit', class: 'btn btn-primary' %>
      <%= link_to "Back", movies_path, class: "btn btn-secondary" %>
    </div>
  </div>
<% end %>

Nest the reviews resource in routes.rb.

resources :movies do
  collection do
    get 'search'
  end
  resources :reviews
end

In movies show page add a link to write a review.

<%= link_to 'Write a Review', new_movie_review_path(@movie) %>

Change the movie form partial to use nested routes for the review url.

<%= form_for([@movie, review]) do |f| %>

Find the parent in the reviews new action.

def new
  @movie = Movie.find(params[:movie_id])
  @review = Review.new
end

Now the new review form will render. Change the movie show page to add a link to write a review and display any existing reviews.

<div class="col-md-7 col-md-offset-1">
  <h1 class="review_title"><%= @movie.title %></h1>
  <% if @reviews.blank? %>
    <h3>No reviews just yet, would you like to add the first!</h3>
    <%= link_to "Write Review", new_movie_review_path(@movie), class: "btn btn-primary" %>
  <% else %>
    <% @reviews.each do |review| %>
      <div class="reviews">
        <div class="star-rating" data-score= <%= review.rating %> ></div>
        <p><%= review.comment %></p>
      </div>
    <% end %>
  <% end %>
</div>

Declare the associations in the review and movie models.

class Review < ApplicationRecord
  belongs_to :movie
end
class Movie < ApplicationRecord
  has_many :reviews
end

Add the movie_id foreign key to reviews table.

rails g migration add_movie_id_to_reviews

Change the generated migration file.

class AddMovieIdToReviews < ActiveRecord::Migration[5.0]
  def change
    add_column :reviews, :movie_id, :integer
  end
end

Migrate the database.

rails db:migrate

You can now rate the movie when writing a review using a number field. We will now see how to replace the number field with stars using Raty jQuery plugin.

Setup jQuery Raty Plugin

You can see a demo of jQuery raty plugin at raty demo. Download the jquery raty plugin zip file from jquery raty home page. Copy the jquery.raty.js to vendor/assets/javascripts folder. Copy the star images to app/assets/images folder. Add the raty jQuery library in application.js.

//= require jquery.raty

after

//= require jquery

Add the following javascript to the bottom of movie show page.

<script>
    $('.star-rating').raty({
      path: '/assets/',
      readOnly: true,
      score: function() {
            return $(this).attr('data-score');
    }
  });
</script>

Reload the movie show page. You will now see the rating displayed in star format. Let's now make the movie review page use rating star when reviewing a movie. Change the rating from number field to star rating in the rating review form partial.

<div class="form-group row">
  <%= f.label :rating, class: 'col-sm-2 form-control-label' %>
  <div class="col-sm-6" id='star-rating'></div>
</div>

Add the javascript that will submit the user selected star rating to the server at the end of the movie form partial.

<script>
$('#star-rating').raty({
    path: '/assets',
    scoreName: 'review[rating]'
});
</script>

Display Average Rating

Let's now display average rating in the movie show page.

<div class='star-rating' data-score=<%= @avg_rating %>></div>
<em><%= "#{@reviews.size} reviews" %></em>

Calculate average rating in the movie show action.

def show
  @reviews = @movie.reviews.to_a
  @avg_rating = if @reviews.blank?
    0
  else
    @movie.reviews.average(:rating).round(2)
  end
end

You will now see the average rating for a movie under the movie poster. You can download the code for this article from FilmBuff

Summary

In this article, you learned how to use jQuery Raty, star rating plugin with Twitter Bootstrap 4 in Rails 5 app.


Related Articles


Create your own user feedback survey