基于AJAX与Symfony的点赞计数器无刷新更新问题求助
问题描述
网站点赞系统已搭建,点击时点赞按钮可在空心/实心状态间切换,后端功能运行正常,但点赞计数器无法实现无刷新更新,作为AJAX新手尝试网上示例未解决,附上代码求助。
代码展示
CommentLikeController.php
/** * @Route("/comments/like-unlike", name="app_comment_like_unlike") */ public function like(Request $request, EntityManagerInterface $entityManager): Response { if ($request->getMethod() === 'POST'){ //retrieve commentId from AJAX request $commentId = $request->request->get('entityId'); //check if comment exists $comment = $entityManager->getRepository(Comment::class)->findOneBy(['id'=> $commentId]); // return void if not if (!$comment){ return new JsonResponse(); } // retrieve csrf Token from AJAX request $submittedToken = $request->request->get('csrfToken'); // check if it's valid if ($this->isCsrfTokenValid('comment' . $comment->getId(), $submittedToken)){ $user = $this->getUser(); $commentAlreadyLiked = $entityManager->getRepository(CommentLike::class) ->findOneBy(['user' => $user, 'comment' => $comment]); // if user unlikes the comment, remove it from DB if($commentAlreadyLiked){ $entityManager->remove($commentAlreadyLiked); $entityManager->flush(); return new JsonResponse(); // if user likes the comment, add record to DB } else{ $like = new CommentLike(); $like->setUser($user); $like->setComment($comment); $entityManager->persist($like); $entityManager->flush(); } } } return new JsonResponse(); }
index.html.twig
<div class="js-likes-unlikes"> <a href="{{ path('app_comment_like_unlike') }}" id="comment-like" data-entity-id="{{ comment.id }}" data-csrf-token="{{ csrf_token('comment' ~ comment.id) }}" data-liked="{{ comment.commentLikes.isEmpty == true ? '0' : '1' }}" data-likes-counter="{{ comment.commentLikes.count }}" class="btn-like"> <i class="♡ {{ comment.commentLikes.isEmpty == true ? '' : 'liked' }}"> {{ comment.commentLikes.isEmpty == true ? '♡' : '♥' }} </i> </a> <span class="counter">{{ comment.commentLikes.count }}</span> </div> {% block javascripts %} {{ parent() }} <script src="{{ asset('js/comments_like_unlike.js') }}"></script> {% endblock %}
comments_like_unlike.js
$(document).on('click', '#comment-like', function (e) { e.preventDefault(); var url = $(this).attr('href'); var entityId = $(this).attr('data-entity-id'); var csrfToken = $(this).attr('data-csrf-token'); var isLiked = $(this).attr('data-liked'); var likesCounter = $(this).attr('data-likes-counter'); if (isLiked === '0') { $(this).attr('data-liked', 1); $('.♡').addClass('liked').text('♥') likesCounter++; } else { $(this).attr('data-liked', 0); $('.♡').removeClass('liked').text('♡') likesCounter--; } $.ajax({ type: 'POST', dataType: 'json', data: {'entityId': entityId, 'csrfToken': csrfToken, 'likesCounter': likesCounter}, url: url, success: function () { console.log("success"); }, error: function () { } }); });
解决方案及解释
1. 后端返回更新后的点赞数
当前后端仅返回空的JsonResponse,前端无法获取准确的最新点赞数(多人同时操作时,前端本地计算会有误差)。修改控制器,在操作完成后返回当前评论的点赞总数:
// 替换原有的return new JsonResponse(); // 取消点赞逻辑 if($commentAlreadyLiked){ $entityManager->remove($commentAlreadyLiked); $entityManager->flush(); $likesCount = $comment->getCommentLikes()->count(); return new JsonResponse(['likesCount' => $likesCount]); } else{ // 点赞逻辑 $like = new CommentLike(); $like->setUser($user); $like->setComment($comment); $entityManager->persist($like); $entityManager->flush(); $likesCount = $comment->getCommentLikes()->count(); return new JsonResponse(['likesCount' => $likesCount]); }
2. 修正前端DOM选择器
当前$('.♡')会选中页面所有爱心图标,应仅操作当前点击按钮内的图标,改用$(this).find('.♡')定位。
3. 更新计数器DOM元素
之前仅修改了变量likesCounter,未更新页面上的<span class="counter">元素,需找到对应元素并更新文本,同时优先使用后端返回的准确数值。
4. 修正计数器变量类型
从data-属性获取的值是字符串类型,直接++/--会导致字符串拼接,需转成整数。
修改后的JS代码
$(document).on('click', '#comment-like', function (e) { e.preventDefault(); const $this = $(this); const url = $this.attr('href'); const entityId = $this.attr('data-entity-id'); const csrfToken = $this.attr('data-csrf-token'); let isLiked = $this.attr('data-liked'); // 转成整数类型 let likesCounter = parseInt($this.attr('data-likes-counter'), 10); // 先临时更新UI提升体验 if (isLiked === '0') { $this.attr('data-liked', '1'); $this.find('.♡').addClass('liked').text('♥'); likesCounter++; } else { $this.attr('data-liked', '0'); $this.find('.♡').removeClass('liked').text('♡'); likesCounter--; } $.ajax({ type: 'POST', dataType: 'json', data: {'entityId': entityId, 'csrfToken': csrfToken}, // 无需传likesCounter给后端,后端自行计算准确值 url: url, success: function (response) { // 用后端返回的准确值更新计数器和data属性 const finalCount = response.likesCount; $this.attr('data-likes-counter', finalCount); $this.siblings('.counter').text(finalCount); }, error: function () { // 请求失败时回滚UI状态 if (isLiked === '0') { $this.attr('data-liked', '0'); $this.find('.♡').removeClass('liked').text('♡'); } else { $this.attr('data-liked', '1'); $this.find('.♡').addClass('liked').text('♥'); } alert('点赞操作失败,请重试'); } }); });
额外优化:避免重复ID
若页面有多个评论,id="comment-like"会重复,建议改为class:class="comment-like btn-like",JS选择器改为$(document).on('click', '.comment-like', ...),避免ID冲突导致仅第一个评论的点赞按钮生效。
内容的提问来源于stack exchange,提问作者Officer Pulaski
相关产品推荐
相关产品推荐

