Reputation: 59
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
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