Skip to content

Instantly share code, notes, and snippets.

@andrewhaines
Last active October 20, 2019 09:21
Show Gist options
  • Save andrewhaines/5cd5fa6cccb2580ecac1b4e43ec28d60 to your computer and use it in GitHub Desktop.
Save andrewhaines/5cd5fa6cccb2580ecac1b4e43ec28d60 to your computer and use it in GitHub Desktop.
Rails UJS toggling strategy

Rails UJS toggling strategy

For 'has_many :through' associations

This strategy can be used for creating / removing associations between two objects in a Rails app using Ajax / UJS. For example, setting up tag associations (i.e., one tag name applied to many individual posts) or favorites (i.e., many users saving personal favorites against global objects).

It's designed to work in a list / table format, where you're creating and removing associations in a single page format. To initiate, make sure the list / table partial for each "owner" item contains the content from _item-partial.html.erb. A similar setup should also work in non-list / table formats, as long as each instance of the owner artifact includes the classes and data attributes required by the JavaScript partials.

For boolean toggling

If using to modify a single field on a record (e.g., a boolean) rather than creating separate associations, the controller setup would need to be adjusted to work with the update method on the "owner" class directly:

def update
  @thing = Thing.find(params[:id])

  if params[:attribute]
    @thing.update(attribute: params[:attribute])
  end

  respond_to do |format|
    if @thing.update(thing_params)
      format.js
    end
  end
end

And links would need to supply method: :patch:

<%= link_to thing_path(@thing, thing: { attribute: value} ), method: :patch, remote: true, class: 'thing-status save-thing', data: { turbolinks: false, thing_id: @thing.id } do %><i class="far fa-check-square"></i><% end %>

The create.js.erb and destroy.js.erb files could be consolidated to a single update.js.erb file to accept either case.

# For using in initial view, e.g., a table list item that will offer option to create / remove associations via `Thing`
<% if Thing.find_by(owner_id: @owner.id, association_id: i.id) %>
<%= link_to thing_path(Thing.find_by(owner_id: @owner.id, association_id: i.id).id), method: :delete, remote: true, class: 'thing-status remove-thing', data: { turbolinks: false, association_id: i.id } do %><i class="far fa-check-square"></i><% end %>
<% else %>
<%= link_to things_path(owner_id: @owner.id, association_id: i.id), method: :post, remote: true, class: 'thing-status save-thing', data: { turbolinks: false, association_id: i.id } do %><i class="far fa-square"></i><% end %>
<% end %>
$('.thing-status.create-thing[data-association-id="<%= @thing.association_id %>"]').bind('ajax:success', function() {
$(this).replaceWith("<%= j render('remove_thing_link') %>");
// console.log("create.js invoked <%= @thing.association_id %>"); // For testing
});
$('.thing-status.remove-thing[data-association-id="<%= @thing.association_id %>"]').bind('ajax:success', function() {
$(this).replaceWith("<%= j render('add_thing_link') %>");
// console.log("destroy.js invoked <%= @thing.association_id %>"); // For testing
});
resources :things, only: [:create, :destroy]
class Thing < ApplicationRecord
belongs_to :owner
end
def create
@thing = Thing.new(owner_id: params[:owner_id], association_id: params[:association_id])
respond_to do |format|
if @thing.save
format.js
end
end
end
def destroy
@thing = Thing.find(params[:id])
@thing.destroy
respond_to do |format|
format.js
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment