0

I am making a forum with votes currently.

If I go into a post, it would look like this.

  • The Post
    • Answer
    • Answer
    • Answer
    • and so on...

I have methods to call meteor.methods when upvote and downvote, undownvote and unvote is clicked. It inserts the user to downvoters, upvoters, inc or inc -1 votes etc.

Then I have the following code to disable the opposite button if clicked.

For example, when upvote is clicked, the downvote is disabled. when downvote is clicked, upvote is disabled.

Template.answerItem.rendered

Template.answerItem.rendered = function() {
    if( $('#upvote').hasClass('unvote') ) {
        $('#downvote').attr('disabled', 'disabled');
    }

    if( $('#downvote').hasClass('undownvote')) {
        $('#upvote').attr('disabled', 'disabled');
    }
}

The problem is that if I have more than one answers in a thread, this will apply to all the answerItems instead of just the one answerItem that it has been applied to.

For example, if I click upvote in answer1, downvote will be disabled in both answer1 and answer2.

answers are loaded as followings

Within the thread(post)

{{#each answers}}
    {{> answerItem}}
{{/each}}

How would I make it so that the code I have only applies to the(one) answerItem so that it functions separately for each answerItem?

EDIT1

updated information(code)

HTML voting snippet of Template.answerItem

<div class = "voting-container">
    <button id="upvote" class="upvote {{upvotedClass}}">
        <span class="glyphicon glyphicon-triangle-top"></span>
    </button>

    <div class = "voteCount-container"> 
        <span>{{votes}}</span>  <!--To retrieve votecount from answers collection-->
    </div>

   <button id="downvote" class="downvote {{downvotedClass}}">
        <span class="glyphicon glyphicon-triangle-bottom"></span>
    </button>               
</div>

Template.answerItem.rendered

Template.answerItem.rendered = function() {
    var $downvote = this.$('.downvote');
    var $upvote = this.$('.upvote');

    if($upvote.hasClass('unvote'))
        $downvote.attr('disabled', 'disabled');

    if($downvote.hasClass('undownvote'))
        $upvote.attr('disabled', 'disabled');
}

Template.answerItem.helpers

Template.answerItem.helpers({

    upvotedClass: function() {
        var userId = Meteor.userId();
        if (userId && !_.include(this.upvoters, userId)) {
            return 'upvotable';
        }  else {
            return 'unvote';
        }    
    },
    downvotedClass: function() {
        var userId = Meteor.userId();
        if (userId && !_.include(this.downvoters, userId)) {
            return 'downvotable';
        } else {
            return 'undownvote';
        }
    }

});

Template.answerItem.events

Template.answerItem.events({


    'click .upvotable': function(e) {
    e.preventDefault();
    $('.downvotable').prop('disabled', true);
    Meteor.call('upvoteAnswer', this._id);
    },

    'click .unvote': function(e) {
    e.preventDefault();
    $('.downvotable').prop('disabled', false);
    Meteor.call('unvoteAnswer', this._id);
    },

    'click .downvotable': function(e) {
    e.preventDefault();
    $('.upvotable').prop('disabled', true);
    Meteor.call('downvoteAnswer', this._id);
    },

    'click .undownvote': function(e) {
    e.preventDefault();
    $('.upvotable').prop('disabled', false);
    Meteor.call('undownvoteAnswer', this._id);
    }

})
Sang Yoo Kim
  • 355
  • 1
  • 15

2 Answers2

1

You can use this and then get the relevant .downvote element using parent() or siblings(), provided they are, indeed, siblings. Also, replace your ID's with classes.

Template.answerItem.rendered = function() {
    if( $('.upvote').hasClass('unvote') ) {
        $(this).siblings('.downvote').attr('disabled', 'disabled');
    }

    if( $('.downvote').hasClass('undownvote')) {
        $(this).siblings('.upvote').attr('disabled', 'disabled');
    }
}
dayuloli
  • 13,791
  • 13
  • 58
  • 103
  • This is great advice. I couldn't check yours as an answer because The above answer was first. But thank you so much! – Sang Yoo Kim Mar 17 '15 at 17:08
  • I've tried both answers and it didnt work. I updated my code, please take a look at it if you have to time. Thank you very much :) – Sang Yoo Kim Mar 17 '15 at 17:38
1

HTML requires that you not repeat the same id on the page. Because each answerItem has an #upvote, you'll end up having several rendered simultaneously.

Step 1

Replace your upvote and downvote ids with classes.

Step 2

You can use template.$ to isolate your jQuery selector to only elements in the current template. From the rendered callback, you cause use this.$ like so:

Template.answerItem.rendered = function() {
  var $downvote = this.$('.downvote');
  var $upvote = this.$('.upvote');

  if($upvote.hasClass('unvote'))
    $downvote.prop('disabled', true);

  if($downvote.hasClass('undownvote'))
    $upvote.prop('disabled', true);
}

Also note that .prop('disabled', true) is apparently the correct way to disable an input.

David Weldon
  • 59,944
  • 10
  • 136
  • 139
  • Thank you! I didn't know I could use hasClass for classes :)! – Sang Yoo Kim Mar 17 '15 at 17:09
  • I'm sorry I had to uncheck your answer. I thought it was working, but it did not... I have updated my code for more scrutiny... – Sang Yoo Kim Mar 17 '15 at 17:32
  • 2
    Yeah I'm not sure w/o actually running all the code. In general I think you are storing too much information in the DOM. The solution would be easier if you were manipulating the classes entirely through reactive state rather than by constantly using jQuery (usually that's a sign you are doing something wrong). Also, you should remove the ids from the buttons - again you can't repeat those. – David Weldon Mar 17 '15 at 17:43
  • I have inspected my code and have found out the problem probably lies within `Template.answerItem.events`. I've tried use `this.$` within `answerItem.events`. I get `Uncaught Type Error: undefined is not a function`...Why can't I use `this.$` in `'click .sth': function(e) { }`? – Sang Yoo Kim Mar 17 '15 at 18:16
  • 1
    It's confusing - the context of an event is different than that of rendered. Events can take two arguments: the event object and a template - you need to use the 2nd argument. So something like `function(e, t) {t.$('.downvotable').prop(...)}`. – David Weldon Mar 17 '15 at 18:50
  • Thanks @David Weldon. adding template to the function worked. I think I have a better understanding of HTML and jquery template etc. Thank you so much :) – Sang Yoo Kim Mar 18 '15 at 01:28