How To Dynamically Add and Remove Items with Rails

Matthew Thorry
5 min readSep 6, 2017

--

A screenshot from the events web application I created. Filler text courtesy of Faker Gem using Rupaul & Fresh Prince quotes with some HipsterIpsum for good measure.

I previously wrote about a web application I created using Ruby on Rails. For this app, which I created with hui wang, we made a user show page which had all the events a user had added to their events.

When a user found an event they wanted to attend, they clicked the “Add to my events” button and them BOOM it appears on your show page. Then if you decide to not attend the event any more, click “Remove from my events” and it’s gone!

I spoke a little bit about how using Event Listeners in JavaScript can simplify this process, however that method doesn’t utilize the database at all. In my example here, adding events is not just client side but also stored in the database as one of the user’s events.

Here’s an example of the events show page for New York City. You can see events I already have said I’m attending. I can also add events I’m not attending or buy tickets.

To have this functionality we needed to have a way for the buttons to add the specific event instance to the user’s events.

We achieved this functionality through class associations

In the case of our app, we used the following relationships for the events and users classes

class User < ApplicationRecord
has_many :user_events
has_many :events, through: :user_events
[...]
end
class Event < ApplicationRecord
has_many :user_events
has_many :users, through: :user_events
[...]
end

This establishes a classic has_many through relationship between Users and Events. Now a User doesn’t need to worry about keeping track of their Events and Events don’t need to keep track of all their Users. Instead the UserEvent class handles that:

class UserEvent < ApplicationRecord
belongs_to :event
belongs_to :user
end

Now, we can access a Users events by typing User.events and you can see all the events attached to the User. Similarly you can do Event.users and see all the Users attending. This is great and we definitely used it to our advantage.

Next we created methods that could add or delete events from a User.

These methods were added to the User class since they would be added and deleted from the User and not adding or deleting the actual Event instance. The code for this was fairly simple:

 def add_event(event)
if !self.events.include?(event)
self.events << event
end
end
def delete_event(event)
if self.events.include?(event)
self.events.delete(event)
end
end

Here we are taking the specific Event instance and shoveling it into the User.events array but ONLY if it does not already exist in their events. This is to prevent duplicate events from being in the User’s events.

After the methods are working, add actions to the controllers

This part was a little tricky. We had to Macgyver our actions a bit, so try not to judge. In our Events controller we added the addevent action:

 def addevent
event = Event.find(params[:id])
current_user.add_event(event)
current_user.save
redirect_to user_path(current_user)
end

Then in our Users controller we added the deleteevent action:

 def deleteevent
event = Event.find(params[:id])
current_user.delete_event(event)
current_user.save
redirect_to user_path(current_user)
end

Why were the responsibilities separated like this? Because of our routes 🙄

post '/events/:id', to: 'events#addevent', as: 'addevent'
post '/users/:id', to: 'users#deleteevent', as: 'deleteevent'

Since we were doing POST actions to the same URL (the User), add and delete were not functioning. So we channeled the add event action to the Events controller and delete to the Users controller. This made it so events could be added and deleted. Not ideal but it worked. Suggestions for improvement welcome!

With all the backend functionality up and running, we need to make it visible for the User

This was done through our Views and in a number of different ways. Like I said before, this wasn’t the most DRY code but it did what we wanted!

A screenshot of the “All Events” page as well as the code for it

In the code, I included the “All Events” show page which put every event in every city on the page. To get our buttons to actually utilize the methods and actions we’d written we had to use a little Rails automagic. The most important part of the code is here:

<%= link_to 'Add to my events', addevent_path(event), :method => :post, class: "btn btn-outline-primary"%>

Here we created a link that utilized the addevent_path of this specific event (specified above in the loop through all events) through the :post method. Bootstrap was used for styling to that’s what the class: is.

Now, when the button is clicked the post request is sent through the addevent_path in the Events controller. In the controller the add_event method is called which adds the event to the User’s events array.

With this, a user could add an event! Hallelujah!

For removing an event, we followed a similar structure. Here’s the code from our User show page where you can either ADD or REMOVE events:

<% if current_user == @user %>    <%= link_to 'Remove From My Events', deleteevent_path(event), :method => :post, class: "btn btn-outline-danger"%><% else %>    <%= link_to 'Add to my events', addevent_path(event), :method => :post, class: "btn btn-outline-primary"%> <%end%>

Again, when the button is clicked the post request is sent through the deleteevent_path in the Users controller. In the controller the delete_event method is called which deletes the event from the User’s events array.

And with that, you can dynamically add and remove items from a Users database 🎉

--

--

Matthew Thorry

Former science teacher 👨🏽‍🏫 turned software engineer 👨🏽‍💻 More at: thorry.io