In rails, I want to handle the visual vote icons and the database record upon clicking on a vote button. It seems like it would be a good idea to have both of these in one authoritative location (so that they don't go out of sync), rather than duplicating the control flow logic for the front and back ends in different files.
There is some logic as follows, and I need to handle the front end aspect and the back end aspects. What's he best way to do this in rails?
There is a poll, and there are different options. Each option is given a scope
id.
The following code handles intuitive voting behavior for an option that allows only one option.
model method called by controller
if current_user voted_for? @votable
@votable.unvote_by current_user
@votable.vote_by current_user, scope: params[:vote_type] unless current_user voted_for? @votable, scope: params[:vote_type]
else
@votable.vote_by current_user, scope: params[:vote_type]
end
Now this is fine for the back end, and I need front-end.
asset javascript file
// Detect if record was voted for by current user, obtain #id
// add class 'voted' to child with matching #id
$('#poll .option').on(click, ->
if ($('this').first().class('voted') ) {
$('this').first().removeClass('voted');
} else if ( $('this').siblings('option').first().class('voted') ){
$('this').siblings('option').first().removeClass('voted');
} else {
$('this').first().addClass('voted');
}
Will this work properly with a rails remote: true
link?
html.haml
#poll
= link_to (content_tag :div, class: @voted), vote_path(:vote_type)
= link_to "", vote_path(vote_type: "2"), class=
Using acts_as_votable API to conditionally set class in view. Use CSS to style 'voted'
controller
def show
# can be moved to model
if current_user.voted_on? @votable
@voted = 'voted'
else
@voted = ''
end
I have not used ajax calls in the above. Do I need to? The above javascript seems like it would get very messy very quickly, if I use ajax. It also doesn't prevent multiple voting, or the visual votes going out of sync with what's actually in the database.
Now the above so far duplicates the if/else control flow in the back and front ends. But is it better to combine them in a js.erb file where it does both?
That's why I was thinking it might be better to combine things into one js.erb that handles both front and back. But that doesn't seem like good design either. Perhaps there is a way using ajax to put in more validations and increase robustness? Anyway, this is all just nice-to-haves. As long as it works, that's good.
It seems having a custom js.erb file is not good for using responds_with... I am confused. on how to proceed.
Sorry for the multiple questions within this question. I am just trying to implement an intuitive voting system, and it's not very easy to get bits and pieces of information from different sources.
Thanks in advance.
This is a very generic question. Aw as for the backend you can use some of the gems to do that as it would make your life easier something like acts_as_votable gem for rails.
Then include it at your ActiveRecord model:
class Record < ActiveRecord::Base
acts_as_votable
end
Now you will be able to use a lot of helper methods like:
@record = Record.create!(attributes)
@record.liked_by @user1
@record.downvote_from @user2
@record.vote_by :voter => @user4, :vote => 'bad'
@record.vote_by :voter => @user5, :vote => 'like'
As for your fronend, you can send some Ajax requests with the like of link_to remote: true or form_for remote:true. Then you can use RJS or even doing that manually on the client side using jQuery and change the state of the divs.
I'll demonstrate with code here:
In Controller you'll have an action something like:
def vote
@record = Record.find(params[:post_id])
@record.liked_by current_user
end
Html
<%= link_to 'like', vote_path(@record), class: 'vote', remote: true %>
JS
$('.vote')
.on('ajax:send', function () { $(this).addClass('loading'); })
.on('ajax:complete', function () { $(this).removeClass('loading'); })
.on('ajax:success', function (data) { $(this).html(data.count); });