1

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

1 Answer 1

1

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)%>");
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.