Implementing Ajax Like Button functionality in a Ruby on Rails application

I have a Ruby on Rails project that includes a User model and a Content model, among others. I recently implemented a feature where users can "like" content using the acts_as_votable gem.

Currently, the liking functionality works as expected but it requires a page refresh every time a user clicks the like button.

I'm interested in incorporating Ajax into this feature to allow for updating the like button and counter without refreshing the entire page.

Here's a snippet from my Content -> Show view:

<% if user_signed_in? %>
  <% if current_user.liked? @content %>
      <%= link_to "Dislike", dislike_content_path(@content), class: 'vote', method: :put %>
  <% else %>
      <%= link_to "Like", like_content_path(@content), class: 'vote', method: :put %>
  <% end %>
  <span> · </span>
<% end %>

<%= @content.get_likes.size %> users like this
<br>

The logic in the Content controller for liking/disliking is as follows:

def like
    @content = Content.find(params[:id])
    @content.liked_by current_user
    redirect_to @content
end

def dislike
    @content = Content.find(params[:id])
    @content.disliked_by current_user
    redirect_to @content
end

And in my routes.rb file, I've defined the following routes:

resources :contents do
    member do
        put "like", to: "contents#like"
        put "dislike", to: "contents#dislike"
    end
end

While the liking system is functional, it doesn't update the likes counter or the like button in real-time. To work around this, I currently use redirect_to @content in the controller action.

Is there a way to implement this behavior with an Ajax call? Any suggestions on improving this process?

Answer №1

If you want to implement this functionality, there are several approaches you can take. Here's a simple method:

Initial Steps

  1. To begin with, ensure that Rails UJS and jQuery are included in your application.js file (if they're not already there):

    //= require jquery
    //= require jquery_ujs
    
  2. Add remote: true to your link_to helpers:

    <%= link_to "Like", '...', class: 'vote', method: :put, remote: true %>
    
  3. Ensure that the controller responds appropriately to AJAX requests without redirection:

    def like
      @content = Content.find(params[:id])
      @content.liked_by current_user
    
      if request.xhr?
        head :ok
      else
        redirect_to @content
      end
    end
    

Enhanced Features

If you want to update the display showing how many users have liked the content, follow these steps:

  1. Assign an identifier to the counter value to make it easily retrievable using JavaScript later:

    <span class="votes-count" data-id="<%= @content.id %>">
      <%= @content.get_likes.size %>
    </span>
    users like this
    

    Make sure to use data-id instead of just id. Consider refactoring this snippet into a helper method for reusability.

  2. Update the controller response to provide the count along with additional information for locating the counter on the page (the keys may vary):

    #…
    if request.xhr?
      render json: { count: @content.get_likes.size, id: params[:id] }
    else
    #…
    
  3. Create some CoffeeScript (or other JS) code to handle the AJAX response:

    # This event is triggered by Rails after successful execution of link_to(remote: true)
    $(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
      # The `data` object contains the decoded JSON data
      $(".votes-count[data-id=#{data.id}]").text data.count
      return
    

    Once again, utilize the data-id attribute to update only relevant counters.

Switching Between States

To dynamically change the link text from "like" to "dislike" and back, make the following adjustments:

  1. Modify your view as follows:

    <% if current_user.liked? @content %>
      <%= link_to "Dislike", dislike_content_path(@content), class: 'vote', method: :put, remote: true, data: { toggle_text: 'Like', toggle_href: like_content_path(@content), id: @content.id } %>
    <% else %>
      <%= link_to "Like", like_content_path(@content), class: 'vote', method: :put, remote: true, data: { toggle_text: 'Dislike', toggle_href: dislike_content_path(@content), id: @content.id } %>
    <% end %>
    

    This code could be placed in a helper method for cleaner organization (e.g., vote_link current_user, @content).

  2. Include the following CoffeeScript logic:

    $(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
      # Update the counter
      $(".votes-count[data-id=#{data.id}]").text data.count
    
      # Toggle links
      $("a.vote[data-id=#{data.id}]").each ->
        $a = $(this)
        href = $a.attr 'href'
        text = $a.text()
        $a.text($a.data('toggle-text')).attr 'href', $a.data('toggle-href')
        $a.data('toggle-text', text).data 'toggle-href', href
        return
    
      return
    

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Next.js: An absence of response is observed when a high volume of requests is made on a server-side rendering page with

Currently, I am utilizing Next.js along with a custom server (express js) as demonstrated in the GitHub example. For instance, I have a page located at “/post/[id]” that makes use of Next.js dynamic routing. The issue arises when the number of request ...

Sending Data from PHP Controller to Ajax

I have implemented a Model-View-Controller (MVC) architecture for my website. In this setup, I have a button that triggers an AJAX call to remove an uploaded image from the site. Here is an excerpt from my Model: public function remove_document($document ...

Understanding how to parse an array of arrays in JavaScript can be a

Looking for a function that can extract a value from an array containing multiple arrays? Simply use getValueFromArray(array, [2, 4]) to get the 4th element of the 2d array within the main array. Check out the code snippet below: function getValueFromArr ...

Troubleshooting: Custom JQuery function not functioning as expected

I am currently facing an issue with the jQuery in my website while trying to implement a portfolio element. It seems to be related to the changePortfolio() function, but I am unsure of how to resolve it. $('.projects a[href^="#"]').on('clic ...

Navigating between two table components in React JS

I am a beginner in the world of React and I am struggling with switching between these two tables. Despite consulting the documentation for inline conditional statements, I still couldn't figure it out. My goal is to have the tables switch after click ...

Unable to call component method after exporting with connect in React Redux

My React class component features a method that I would like to access later through the provider. Take a look at the class below: class ContactList extends Component { // props for contact renderContacts = () => { return // something }; ...

Navigating Express HTTP Requests within Apollo Link Context in a NextJS Web Application

Currently, I am in the process of developing a NextJS application and facing a challenge with accessing a cookie to utilize it for setting a Http Header within a GraphQL Request. For this task, I am integrating apollo-link-context. Below is the snippet of ...

There was a mistake: _v.context.$implicit.toggle cannot be used as a function

Exploring a basic recursive Treeview feature in angular4 with the code provided below. However, encountering an error when trying to expand the child view using toggle(). Encountering this exception error: ERROR TypeError: _v.context.$implicit.toggle i ...

The AJAX email submission form is not functioning properly

Recently, I have upgraded my email sign-up form on the website to include validation. However, after adding the validation, the form no longer sends and an error message is not displayed. Upon inspecting, I found the following error: TypeError: null is ...

Why does my JSON variable contain "type" and "data" values instead of something else?

After using JSON.stringify() on my object to save it to a file, I noticed that one of the parameters does not have the expected string value assigned. Instead, it has a "type" and "data". Code: fs.writeFileSync('myjson.json', JSON.stringify(myjs ...

fluctuating random percentage in JavaScript/jQuery

I am currently faced with the challenge of selecting a random number based on a given percentage ranging from 0 to 5. 0 - 25% (25/100) 1 - 25% (25/100) 2 - 20% (20/100) 3 - 15% (15/100) 4 - 10% (10/100) 5 - 5% (5/100) However, there are instances where ...

Navigating AngularJS with multiple external files and folders

Recently dove into Angular and hit a roadblock with routing. I followed the setup instructions, but for some reason it's not functioning as expected. index.html: <!DOCTYPE html> <html lang="en> <head> <meta charset="utf-8> ...

Adding a unique value to an array using JQuery when it does not already exist

In need of assistance with a function that is supposed to add values to an array if they do not already exist. var category_json = new Array(); $.ajax({ type: 'POST', url: "<?php ech ...

Using the Mongoose $or operator with a nested array in query conditions

Here are the schemas I am using: //ProjectModel const ProjectSchema: Schema = new Schema( owner: { type: Schema.Types.ObjectId, ref: "User" }, users: [{type: Schema.Types.ObjectId, ref: "ProjectUser", unique: true }] ); //Project Use ...

Troubleshooting Type Conversion Error in ASP.NET MVC Controller

I have been working on an application that utilizes the following HTML and JavaScript. The user is required to input 5 props and then click on the 'Create' button. Subsequently, the JavaScript code compiles all of these props into a list before s ...

What causes Bootstrap to malfunction when the route contains double slashes?

When using /something, everything works fine, but when switching to /something/somethingelse, Bootstrap fails to function. It seems that the number of "/" characters in the route is causing this issue, rather than the content inside the .ejs file. Here is ...

How can you refresh the source element?

Is there a way to make the browser reload a single element on the page (such as 'src' or 'div')? I have tried using this code: $("div#imgAppendHere").html("<img id=\"img\" src=\"/photos/" + recipe.id + ".png\" he ...

What do you prefer: defining properties with the JSON object or with objectName.property in JavaScript

Can you tell me which approach is considered the best practice? Is it better to use the "this" statement in the following way: var obj = { x: 20, y: 10, width: this.x, height: this.y, render: function () { // renders object on canvas ctx.fi ...

What is the method for eliminating PHP $_SESSION using AJAX?

I am facing an issue with removing an array within a PHP Session variable using AJAX. Here is the process I follow: HTML: <a href="#" onclick="delete_pix(false, '1', false, '1.jpg');">remove</a> JavaScript: functio ...

Combing external JavaScript with React functionality

Hey there, I've been working on merging two projects that I came across recently: https://github.com/danxfisher/MeetEasier and this awesome page https://tympanus.net/Development/Interactive3DMallMap/ After making some changes in the MeetEasier React ...