Creating Like button functionality in Meteor

Making a “Like” system for user Posts is a pretty common task. I’m going to create a very basic one here. It should go without saying, but needs to be said: it’s the basics. If you use it for a model you will need to “battle harden” it, improve it, make it your own.

So, what are the specs of this “Like” system? The user will click on a “Like” button, and the Posts “Like” count will be incremented by one. But, no single user can “Like” a post more than once… that would be cheating, after all.

To create this system, all that is needed is a database, a “Like” button of some fashion, and some form of showing the total “Likes” for the post in question. Our cast of characters:

The Database

I’ll be using MongoDB here, and to keep things simple, “Likes” will be kept within the Post collection, stored with the post to which they relate. Also, I’m going to keep an array of the user’s who have liked this post, so that I can exclude the same user from “Liking” more than once. There are of course other fields in a specific post’s document, but the two I care about here are:

  likes: 0,
  likers: []

The mechanics are simple: If a user has not already “Liked” this specific post, likes will be incremented by 1, and the user’s ID will be stored in the array likers.

The UI

On each post, I need some way for the user to express a “Like”. For the purposes of this article, it will just be a button:

<button type="button" class="js-like-button" data-id="{{_id}} />

Note: the Spacebars inclusion tokens  {{…}}  contain the _id of the current post. We need this to associate the “Like” to the post.

And, to display the number of “Likes” a simple image with a superscript that will indicate the number of “Likes”:

<img src="likes.png"><sup>{{likes}}<sup>

Note: the use of the Spacebars insertion tokens  {{…}}. The likes between them corresponds to the likes field in our MongoDB document.

The Code

Since this is happening as an action paired to a button click event, our
will contain the logic:

(1)   'click .js-like-button': _.throttle( function( e, t ) {
(2)     e.preventDefault();
(3)     var id = $(e.currentTarget).data( 'id' );
(5)     //get this posts array of likers
(6)     var res = Newsfeeds.find( { _id: id}, { likers: 1}).fetch()[0].likers;
(8)     //see if the current user is one of them
(9)     var q = _.find(res, (x) => x == Meteor.userId() );
(11)    //need to disallow same user that liked to like again
(12)    if ( q == Meteor.userId() ) return;
(14)    //otherwise, allow like and save it
(15)    Newsfeeds.update( { _id: id },  { $inc: { likes: 1 } ,  $push: { likers:  Meteor.userId() } });
(17)  }, 1000 ),

Line (1) I’m using the recommended event handler naming system recommended by Meteor: a ‘js’ prepended to the name of the event handler, as a class. Also, I’m using the Underscorejs function throttle to prevent rapid succession click events. This is closed at Line (17) and set to a 1 second interval ( 1000ms ).

Line (3) This grabs the id of the post in question, from the data-id of the button.

Line (6) I want to pull the document for this post, using the id, and return the array of “Likers”, so that I can ensure there isn’t a user trying to bump “Likes” unduly.

Line (9) Using the Underscorejs function find, I want to ascertain if the current user, obtained via Meteor.userId(), has already “Liked” this post. In the return from the MongoDB query contained in the variable res I have the array of all user’s who have already “Liked” this post. Underscorejsfind will iterate through that array, looking for the current user ( Meteor.userId() ).

Line (12) If the result of the Underscorejs find function matches the current user, they cannot like this post again, so I exit. This test as coded is overkill, but written this way for clarity.

Line (15) If I haven’t returned, this user has not “Liked” this post yet, so I increment the likes field, and push the current user’s id onto the array of likers

That wraps it up. A very simplistic “Like” system for posts.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s