Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Paginating users with a follow/unfollow button: Ajax runs into problems because of duplicate class names

Tags:

My users/show.html.erb pages work correctly because there is only one class name displayed on each page. When users are paginated in users/index.html.erb along with follow/unfollow buttons the relationship/js.create.erb and relationship/js.destroy.erb get wacky because there are duplicate class names on the page, (the first paginated user updates regardless of which user is followed/unfollowed and this only fixes itself when the page is refreshed (the actual model still works correctly)).

I did not use the unfollow/follow partials in users/index.html.erb because there is no @user variable in index.html.erb, it paginates @users, so I use user.id.

class RelationshipsController < ApplicationController
before_action :logged_in_user

def create
  @user = User.find(params[: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

**relationships/create.js.erb**
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
$("#followers").html('<%= @user.followers.count %> <br> followers');

**relationships/destroy.js.erb**
$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
$("#followers").html('<%= @user.followers.count %> <br> followers');

**users/index.html.erb** 
<div class="row community">
  <% @users.in_groups_of(3, false).each do |users| %>
    <div class="col-sm-12">
      <% users.each do |user| %>
            <ul class="name-head">
              <% unless current_user == user %>
                <div id="follow_form">
                  <% 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 btn-primary" %>
                    <% 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 %>
            </ul>
          <div class="col-xs-12">
          <div class="col-xs-4">
            <a href="<%= following_user_path(user.id) %>">
              <center><strong id="following" class:"stat">
              <%= user.following.count %><br>
              following
              </strong></center>
            </a>  
          </div>
          <div class="col-xs-4">
            <a href="<%= followers_user_path(user.id) %>">
              <center><strong id="followers" class="stat">
                <%= user.followers.count %><br>
                followers
              </strong></center>
            </a>
          </div>
          <div class="col-xs-4">
              <center><strong id="photo-count" class="stat">
                <%= user.photos.count %><br>
              photos shared
              </strong></center>
          </div>
        </div>
        </div>  
      <% end %>
    </div>  
  <% end %>
</div>
<%= will_paginate %>

**users/show.html.erb** 
.
.
.
      <li class="follow">
        <%= render 'follow_form' if logged_in? %>
      </li>
    </ul>
  </div>
  <div class="col-xs-12">
    <div class="col-xs-4">
      <a href="<%= following_user_path(@user) %>">
        <center><strong id="following" class:"stat">
        <%= @user.following.count %><br>
        following
        </strong></center>
      </a>  
    </div>
    <div class="col-xs-4">
      <a href="<%= followers_user_path(@user) %>">
        <center><strong id="followers" class="stat">
          <%= @user.followers.count %><br>
        followers
        </strong></center>
      </a>
    </div>
    <div class="col-xs-4">
        <center><strong id="photo-count" class="stat">
          <%= @user.photos.count %><br>
        photos shared
        </strong></center>
    </div>
end

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

**_follow.html.erb**
<%= 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 %>

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

Edit: I thought about passing the user_id into the classes, such as:

<div id="follow_form<%="#{user.id}"%>"> 

which created <div id="follow3">

However I do not know how I can pass this into the destroy.js.erb and create.js.erb

I have tried many combinations such as:

 $("#follow_form<%="#{user.id}"%>").html("<%= escape_javascript(render('users/unfollow')) %>");

and

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

however, the code is still does not work and even if it did, it would still need work because changing the create/destroy.js.erb files would break the users/show.html.erb follow/unfollow buttons.

EDIT: I have also found this question which is similar but it does not contain an accepted answer.

Edit for Heroku Logs: This is what I get when I follow/unfollow a user on my users/index.html.erb page:

Started DELETE "/relationships/360" for 184.90.97.154 at 2016-05-13 20:05:17 +0000
Processing by RelationshipsController#destroy as JS
Rendered users/_follow.html.erb (1.6ms)
 (1.1ms)[0m  [1mSELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = $1[0m  [["followed_id", 1]]

Started POST "/relationships" for 184.90.97.154 at 2016-05-13 20:07:26 +0000
Processing by RelationshipsController#create as JS
[1m[36m (1.5ms)[0m  [1mCOMMIT[0m
like image 801
Timmy Von Heiss Avatar asked May 09 '16 19:05

Timmy Von Heiss


1 Answers

I've duplicated your code and, after adding the modifications below, it worked perfectly. (i.e. I don't need a page reload for the "Follow/Unfollow" button to change)

Try the following to see if it works:


users/index.html.erb (only the relevant code here, which is the "Follow/"Unfollow" button)

<ul class="name-head">
  <% unless current_user == user %>
     <%= render partial: 'follow_form', locals: { user: user } %>
  <% end %>
</ul>

users/_follow_form.html.erb

<div id="follow_form-<%= user.id %>">
  <% if current_user.following?(user) %>
    <%= render partial: 'unfollow', locals: { user: user } %>
  <% else %>
   <%= render partial: 'follow', locals: { user: user } %>
  <% end %>
</div>

users/_unfollow.html.erb

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

users/_follow.html.erb

<%= 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" %>  
<% end %>

relationships/create.js.erb

$("#follow_form-<%= @user.id %>").html("<%= escape_javascript(render(partial: 'users/unfollow', locals: { user: @user })) %>");

relationships/destroy.js.erb

$("#follow_form-<%= @user.id %>").html("<%= escape_javascript(render(partial: 'users/follow', locals: { user: @user })) %>");

Note the use of <%= render partial: "_partial.html.erb", locals: { var_name: var } %>. the optional hash locals: { ....... } allows you to pass in a local variable to a partial you want to render (_partial.html.erb) In this case, you also need to add partial: before the name of the partial you want to render.

This way, your user local variable will become available not only in users/index.html.erb, but also in other files listed above.

like image 72
tg_so Avatar answered Sep 28 '22 02:09

tg_so