Sam Lindstrom
Sam Lindstrom

Reputation: 59

Toggle Button Action OnClick - JQuery + Rails

I would like to implement a Subscribe/Unsubscribe toggle button which creates/destroys a Subscription objectonClick, but I'm having difficulties finding an elegant solution.

I have experimented with multiple ideas, and have come to the conclusion that I need to use Javascript to accomplish this. However, I'm unsure if/how I can simultaneously change the button text and its action when a user clicks it. See button_to 'Subscribe' and button_to 'Unubscribe' below.

Any feedback or advice would be much appreciated!

Artists Index View:

<% @artists.each do |artist| %>
  <tr id="tr_<%=artist.id%>">
    <td><%= artist.name %></td>
    <td><%= artist.artist_data["name"] %></td>
    <td><%= artist.artist_data["facebook_tour_dates_url"] %></td>
    <td><%= artist.artist_data["mbid"] %></td>
    <td><%= artist.artist_data["upcoming_event_count"] %></td>
    <% if artist.is_not_subscribed?(user_id: @user.id, artist_id: artist.id)%>
      <td><%= button_to 'Subscribe', user_subscriptions_path(@user, artist_id: artist.id), method: :post, id: "subscribe_link_#{artist.id}", class: "subscribe", remote: true %></td>
    <% else %>
      <td><%= button_to 'Unsubscribe', user_subscription_path(@user, artist.find_subscription(user_id: @user.id, artist_id: artist.id)), method: :delete, id: "unsubscribe_link_#{artist.id}", class: "unsubscribe", remote: true %></td>
    <% end %>
<% end %>

Subscriptions Controller:

def create
  @user = User.find(params[:user_id])
  @subscription = @user.subscriptions.build(subscription_params)
  @subscription.artist_id = params[:artist_id]

respond_to do |format|
  if @subscription.save
    format.html { redirect_to artists_path, notice: 'Subscription was successfully created.' }
    format.json { render :show, status: :created, location: @subscription }
    format.js
  else
    format.html { render :new }
    format.json { render json: @subscription.errors, status: :unprocessable_entity }
    format.js { render js: @subscription, notice:'Unable to create Subscription' }
   end
 end
end

def destroy
 @subscription = Subscription.find(params[:id])
 @subscription.destroy

 respond_to do |format|
  format.html { redirect_to user_subscriptions_url, notice: 'Subscription was successfully destroyed.' }
  format.json { head :no_content }
  format.js
 end
end

Subscription Model:

class Subscription < ActiveRecord::Base
  belongs_to :artist
  belongs_to :user
  accepts_nested_attributes_for :artist
end

Artist Model:

class Artist < ActiveRecord::Base
  include Subscribable

  has_and_belongs_to_many :events
  has_many :subscriptions
  has_many :users, through: :subscriptions
  validate :artist_already_exists? 
 end

User Model:

class User < ActiveRecord::Base
  include Subscribable

  has_many :subscriptions
  has_many :artists, through: :subscriptions
  accepts_nested_attributes_for :subscriptions
end

Upvotes: 1

Views: 1540

Answers (1)

Sam Lindstrom
Sam Lindstrom

Reputation: 59

Drawing inspiration from this blog post, I was able to implement the following solution. If you have feedback or advice on how to better design this, don't be shy. I would appreciate your thoughts.

TLDR: Added a link_to_toggle method to clean up the view. Utilized Javascript's replaceWith to call link_to_toggle on each link click.

views/artists/index.html.erb

  <% @artists.each do |artist| %>
      <tr id="tr_<%=artist.id%>">
        <td><%= artist.name %></td>
        <td><%= artist.artist_data["name"] %></td>
        <td><%= artist.artist_data["facebook_tour_dates_url"] %></td>
        <td><%= artist.artist_data["mbid"] %></td>
        <td><%= artist.artist_data["upcoming_event_count"] %></td>
        <td><%= link_to_toggle_user_subscription(user:@user, artist:artist)%></td> 
        <td><%= link_to 'Edit', edit_artist_path(artist) %></td>
        <td><%= link_to 'Destroy', artist, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>

helpers/subscriptions_helper.rb

module SubscriptionsHelper

  def link_to_toggle_user_subscription(user:user, artist:artist)

    if user.is_subscribed?(user_id: user.id, artist_id: artist.id)
      link_to('Unsubscribe', user_subscription_path(@user, artist.find_subscription(user_id: @user.id, artist_id: artist.id), artist_id: artist.id), 
              method: :delete, 
              id: "unsubscribe_link_#{artist.id}",
              class: "unsubscribe",
              remote: true)
    else
      link_to('Subscribe', user_subscriptions_path(@user, artist_id: artist.id), 
              method: :post, 
              id: "subscribe_link_#{artist.id}", 
              class: "subscribe", 
              remote: true)
    end
  end

end

controllers/subscriptions_controller.rb

 def create
    @user = current_user
    @artist = Artist.find(params[:artist_id])
    @subscription = @user.subscriptions.build(subscription_params)
    @subscription.artist_id = params[:artist_id]

    respond_to do |format|
      if @subscription.save
        format.html { redirect_to artists_path, notice: 'Subscription was successfully created.' }
        format.json { render :show, status: :created, location: @subscription }
        format.js
      else
        format.html { render :new }
        format.json { render json: @subscription.errors, status: :unprocessable_entity }
        format.js { render js: @subscription, notice:'Unable to create Subscription' }
      end
    end
  end

 def destroy
    @user = current_user
    @subscription = Subscription.find(params[:id])
    @artist = Artist.find(params[:artist_id])    
    @subscription.destroy

    respond_to do |format|
      format.html { redirect_to user_subscriptions_url, notice: 'Subscription was successfully destroyed.' }
      format.json { head :no_content }
      format.js
    end
  end

views/subscriptions/create.js.erb

$('#subscribe_link_<%[email protected]%>').replaceWith("<%=j link_to_toggle_user_subscription(user:@user, artist:@artist)%>");

views/subscriptions/destroy.js.erb

$('#unsubscribe_link_<%[email protected]%>').replaceWith("<%=j link_to_toggle_user_subscription(user:@user, artist:@artist)%>");

Upvotes: 1

Related Questions