简体   繁体   中英

Like Unlike toggle button with jQuery's AJAX

Goal: I'm trying to create a button that allows users to like posts on a website, (similar to the way Facebook does it), which would also increment/decrement the number of likes besides the button as well.

Issue: Everything works well except in one edge case. If the user has already liked the post, he can unlike it but no longer like it again. It seems like the like/unlike toggle is not working and the browser is only sending an 'unlike' request to the server. If the user has never previously like the image, the like/unlike toggle seems to work just fine.

I make use of the post's data attribute to toggle like/unlike requests by making manipulations on these attributes. I'm currently using PHP through a Laravel framework, and jQuery for my front end manipulations.Below is an example of my code.

favorite.js file

$(function(){

    $('.favorite-button').click(function(){
        var $this=$(this);
        var post_id=$this.data('postId');

        $.ajaxSetup({
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                }
        });



        if($this.data('favoriteId')){
            //decrement the favorite count
            count=$this.siblings('.favorite-count');
            count.html(parseInt(count.html())-1);

            //send ajax request
            var fav_id=$this.data('favoriteId');
            $this.removeData('favoriteId');
            $.ajax({
                url:'/post/'+post_id+'/favorite/'+fav_id,
                type:'DELETE',
                success: function(result){
                    console.log('post was unfavorited');
                },
                error: function(){
                    console.log('error: did not favorite the post.');
                }
            });
        }


        else{
            //update the favorite count
            count=$this.siblings('.favorite-count');
            count.html(parseInt(count.html())+1);

            //send ajax post request
            $.ajax({
                url:'/post/'+post_id+'/favorite',
                type:'POST',
                success: function(result){
                    //update the data attributes
                    $this.data('favoriteId',result['id']);
                    console.log(result);
                    console.log('post was favorited');

                },
                error: function(){
                    console.log('error: did not favorite the post.');
                }
            });
        }
    });
});

The HTML file

<div class="pull-right">
    <span class="marginer">
        @if(Auth::guest() || $post->favorites->where('user_id',Auth::user()->id)->isEmpty())
            <i  data-post-id="{{ $post->id }}" class="fa fa-heart fa-lg favorite-button"></i>
        @else
            <i  data-favorite-id="{{ Auth::user()->favorites()->where('post_id',$post->id)->first()->id }}" data-post-id="{{ $post->id }}" class="fa fa-heart fa-lg favorite-button"></i>
        @endif

        <span class="favorite-count">{{ $post->favorites->count() }}</span>
    </span>
</div>

Aside from solving my issue, if you think I am not conforming with best practices with this task, please do comment. I'd like to hear your opinion.

Instead of trying to make use of jQuery, try to simplify the action of "like/unlike/favorite/count" as much as possible

  • Avoid querying from blade templates and just send data to the view

Here is how i would approach this problem instead of letting jQuery do the heavy lifting

HTML/rendered

    <button  class='likebutton' data-identifier='1' data-state='liked'>Like</button>
<button class='favoritebutton' data-identifier='1' data-state='favorited'>Favorite</button>
<span class='count counts-1'>123</span>

Blade

<button  class='likebutton' data-identifier='{{!! Post->ID !!}' data-state='{{!! /*<something to see if user liked this>*/?'liked':'unliked' !!}'>{{!! <something to see if user liked>?'liked':'unliked' !!}</button>
<button class='favoritebutton' data-identifier='{{!! Post->ID !!}' data-state='{{!! /*<something to see if user liked>*/?'favorited':'notfavorited' !!}'>{{!! <something to see if user liked>?'favorited':'notfavorited' !!}</button>
<span class='count counts-{{!! $Post->ID !!}}'>{!! $Post->totallikes !!}</span>

JS

$.ajaxSetup({headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}});

function UserLike(_event){
var state = $(_event.target).data('state');
var postID = parseInt($(_event.target).data('identifier'));
console.log('Status => '+$(_event.target).data('state'));
  $.post('/posts/metadata/likes',{
    'postID':postID
  },function(res){
    $(_event.target).data('state',res.state);
    $(_event.target).text(res.text);
    $('.counts-'+postID).text(res.totallikes);
  }).fail(function(){
    /* do something on fail */
  });
}
function UserFavorite(_event){
var postID = parseInt($(_event.target).data('identifier'));
  $.post('/user/metadata/favorites',{
    'postID':postID
  },function(res){
    $(_event.target).data('state',res.state);
    $(_event.target).text(res.text);
  }).fail(function(){
    /* do something on fail */
  });
}

$("body").on('click','.likebutton',function(_event){    UserLike(_event);   });
$("body").on('click','.favoritebutton',function(_event){    UserFavorite(_event);   });

PHP

// Routes

Route::post('/posts/metadata/likes',function(){
    // auth check
    // handle state/likes
    // ex. response
    // response with json {'state':notliked,'text':'<translated string>','postID':1}
});

Route::post('/user/metadata/favorites',function(){
    // auth check
    // handle state/favorites
    // response with json {'state':favorited,'text':'<translated string>,'postID':1}
});

I would suggest replacing $this.data('favoriteId'); by $this.attr("data-favourite-id") everywhere. It made the difference for me. check this codepen http://codepen.io/jammer99/pen/PNEMgV

However I have no idea why your solution does not work

JQuery data('favoriteId') does not set the attribute data-favourite , it's a runtime only setting, it becomes property of the elements, not attrubutes (That's not the same).

Jquery data can hence not be set by server side code.

You can read more it in the jquery doc for .Prop() : http://api.jquery.com/prop/

There's an explanation about the difference. EDIT: Made the bold sentence!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM