Question

TLDR: Trying to add a follow/unfollow button to the Users#Index action but when clicking on the button nothing happens.

I have followed Michael Hartl's Rails Tutorial app and now that it's complete I want to add extra functionality for the Follow/Unfollow button. Currently, you can only follow/unfollow a user after visiting their profile page thru the Users Show action. I've attempted to expand this functionality to the Index page of the Users controller so that instead of having to visit each user's profile page in order to follow/unfollow them, a follow button will also appear next to their name in the Index view.

The code that works initially for the follow/unfollow button on the Users Show action:

users_controller.rb

https://gist.github.com/anonymous/9602598

show.htlm.erb for users view

https://gist.github.com/anonymous/9602616

_follow_form.htlm.erb partial for users view

<% unless current_user?(@user) %>
  <div id="follow_form">
  <% if current_user.following?(@user) %>
    <%= render 'unfollow' %>
  <% else %>
    <%= render 'follow' %>
  <% end %>
  </div>
<% end %>

_follow.html.erb partial for users view

<%= form_for(current_user.relationships.build(followed_id: @user.id),
             remote: true) do |f| %>
  <div><%= f.hidden_field :followed_id %></div>
  <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>

_unfollow.html.erb partial for users view

<%= form_for(current_user.relationships.find_by(followed_id: @user.id),
             html: { method: :delete },
             remote: true) do |f| %>
  <%= f.submit "Unfollow", class: "btn btn-large" %>
<% end %>

create.js.erb inside of my relationships view

$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>")
$("#followers").html('<%= @user.followers.count %>')

destroy.js.erb inside of my relationships view

$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>")
$("#followers").html('<%= @user.followers.count %>')

index.html.erb inside of the users view

  <%= render @users %>

_user.html.erb partial inside of users view

<li>
  <%= gravatar_for user, size: 52 %>
  <%= link_to user.name, user %>
  <% if current_user.admin? && !current_user?(user) %>
    | <%= link_to "delete", user, method: :delete,
                                  data: { confirm: "You sure?" } %>
  <% end %>

I've tried a few different approaches that so far have been fruitless

First I tried simply rendering the follow_form partial inside of the _user partial just like it's done for the User Show view

_user.html.erb partial inside of users view

    <%= render 'follow_form' if signed_in? %>

this resulted in the following error: NoMethodError in Users#index "undefined method `id' for nil:NilClass" and it hilights the following line of code inside of the follow_form partial

<% if current_user.following?(@user) %>

After mucking around with the variables and the controller for a few hours I finally managed to get the buttons to show properly on the index page with the following code inside of the _user.html.erb partial

<li>
  <%= gravatar_for user, size: 52 %>
  <%= link_to user.name, user %>
  <% if current_user.admin? && !current_user?(user) %>
    | <%= link_to "delete", user, method: :delete,
                                  data: { confirm: "You sure?" } %>
  <% end %>
    <% if current_user.following?(user) %>
      <%= form_for(@current_user.relationships.find_by(followed_id: @users),
             html: { method: :delete },
             remote: true) do |f| %>
        <%= f.submit "Unfollow", class: "btn btn-large" %>
      <% end %>
    <% else %>
      <%= form_for(current_user.relationships.build(followed_id: @users),
             remote: true) do |f| %>
        <div><%= f.hidden_field :followed_id %></div>
      <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
      <% end %>
    <% end %>
</li>

This code however, only shows the correct follow/unfollow buttons for each user but when you click a button nothing happens. I also attempted to edit the JS files with the following code

create.js.erb

$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>")
$("#user").html("<%= escape_javascript(render('users/unfollow')) %>")
$("#followers").html('<%= @user.followers.count %>')

destroy.js.erb

$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>")
$("#user").html("<%= escape_javascript(render('users/follow')) %>")
$("#followers").html('<%= @user.followers.count %>')

This didn't have any effect.

The more I tinker with different parts of the code the more it's confusing me. Here is my relationships controller for good measure.

relationships_controller.rb

class RelationshipsController < ApplicationController
  before_action :signed_in_user

  def create
    @user = User.find(params[:relationship][:followed_id])
    current_user.follow!(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end

  def destroy
    @user = Relationship.find(params[:id]).followed
    current_user.unfollow!(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end
end

I know it's alot but if anyone can help shed some light on my situation I would be very grateful. This is my first venture outside of tutorials and I figured it would be a good idea to try and add features to an already working project instead of start something from scratch.

Was it helpful?

Solution 2

Use the following code in _user.html.erb partial:

<li>
  <%= gravatar_for user, size: 52 %>
  <%= link_to user.name, user %>
  <% if current_user.admin? && !current_user?(user) %>
    | <%= link_to "delete", user, method: :delete,
                                  data: { confirm: "You sure?" } %>
  <% end %>
    <% if current_user.following?(user) %>
      <%= form_for(current_user.relationships.find_by(followed_id: user.id),
             html: { method: :delete },
             remote: true) do |f| %>
        <%= f.submit "Unfollow", class: "btn btn-large" %>
      <% end %>
    <% else %>
      <%= form_for(current_user.relationships.build(followed_id: user.id),
             remote: true) do |f| %>
        <div><%= f.hidden_field :followed_id %></div>
      <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
      <% end %>
    <% end %>
</li>

I made couple of changes to the code in form_for method call in 2 places:

@current_user should be current_user.

Secondly, followed_id: @users should be followed_id: user.id

OTHER TIPS

Just in case anybody was on this question and was still looking for the correct Ajax code. This works with the existing controller and nothing else will have to be changed.

create.js.erb

    var indexed_form = "follow_form_<%=@user.id%>"
    if ( document.getElementById(indexed_form) ){
        var jquery_indexed_form = "#" + indexed_form
        $(jquery_indexed_form).html("<%= escape_javascript(render('users/unfollow')) %>");
    } 
    else {
        $("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
        $("#followers").html('<%= @user.followers.count %>');
    }

destory.js.erb

    var indexed_form = "follow_form_<%=@user.id%>"
    if ( document.getElementById(indexed_form) ){
        var jquery_indexed_form = "#" + indexed_form
        $(jquery_indexed_form).html("<%= escape_javascript(render('users/follow')) %>");
    }
    else {
        $("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
        $("#followers").html('<%= @user.followers.count %>');
    }

_user.html.erb

    <div class="col-md-2">
          <% unless current_user?(user) %>
            <% form_id = user.id.to_s %>

              <div id="follow_form_<%= form_id %>">
                <% if current_user.following?(user) %>
                    <%= form_for(current_user.active_relationships.find_by(followed_id: user.id),
                  html: { method: :delete },
                  remote: true) do |f| %>                       
              <%= f.submit "Unfollow", class: "btn" %>
            <% end %>

        <% else %>
            <%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
              <div><%= hidden_field_tag :followed_id, user.id %></div>              
              <%= f.submit "Follow", class: "btn btn-primary" %>
            <% end %>

        <% end %>

      </div>
  <% end %>

</div>

This is my first post. Yay! Hope it helps someone!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top