Spring框架下JSP页面实现评论单页动态编辑的方案咨询
实现Spring+JSP页面内评论编辑功能
问题描述
我是Web开发新手,正在用Spring Framework开发类Pinterest的Web应用,支持用户注册、发布图片、评论、点赞等功能,数据存储在MySQL数据库。现在需要实现页面内编辑评论的功能:用户点击评论的编辑按钮后,评论内容直接转为可编辑的输入框,无需跳转至单独表单页。
之前尝试用布尔变量editPressed做条件渲染,但逻辑理不清,也不确定是否应该用JS实现,同时不清楚JS如何处理Spring的form:form标签。以下是相关代码片段:
JSP代码
<c:forEach var="eachComment" items="${allCommentsByPhotoId}"> <div class="card-text d-flex gap-2 mb-4"> <img class="user-comment-card" src="https://cdn-icons-png.flaticon.com/512/1053/1053244.png" alt="" /> <div> <div class="mb-2"> <c:choose> <c:when test="${ editPressed == false }"> <c:out value="${eachComment.user.getFirstName()} says: ${ eachComment.getComment() }"></c:out> </c:when> <c:otherwise> <span class="username"> <c:out value="${eachComment.user.getFirstName()}"></c:out>: </span> <c:out value="${ eachComment.getComment()}"></c:out> </c:otherwise> </c:choose> </div> <div class="d-flex align-items-center gap-3"> <c:if test="${ eachComment.user.getId() == currentUser.id }"> <form:form action="/delete/${eachComment.getId()}" method="delete"> <p class="submit"> <input class="btn btn-danger del-cmt-btn" type="submit" value="Delete" /> </p> </form:form> <form:form action="/edit/comment/${eachComment.getId()}"> <p class="submit"> <input class="btn btn-secondary edit-cmt-btn" type="submit" value="Edit" /> </p> </form:form> </c:if> </div> </div> </div> </c:forEach>
Comment实体类
@Entity @Table(name="user_comments_on_photo") public class Comment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String comment; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="user_id") private User user; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="photo_id") private Photo photo; @ManyToMany(fetch=FetchType.LAZY) @JoinTable( name="user_likes_comment", joinColumns = @JoinColumn(name="comment_id"), inverseJoinColumns = @JoinColumn(name="user_id") ) private List<User> usersWhoLikeComment; @Column(updatable=false) @DateTimeFormat(pattern="yyyy-MM-dd") private Date createdAt; @DateTimeFormat(pattern="yyyy-MM-dd") private Date updatedAt; }
User实体类
@Entity public class User { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @Size(min=3) private String firstName; @Size(min=3) private String lastName; @Email private String email; @Size(min=8) private String password; @Transient private String passwordConfirmation; @Column(updatable=false) private Date createdAt; private Date updatedAt; //RELATIONSHIPS @OneToMany(mappedBy="user", fetch = FetchType.LAZY) private List<Photo> photos; @ManyToMany(fetch = FetchType.LAZY) @JoinTable( name = "user_likes_photo", joinColumns = @JoinColumn(name="user_id"), inverseJoinColumns = @JoinColumn(name="photo_id") ) private List<Photo> likedPhotos; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List<Comment> userComments; @ManyToMany(fetch=FetchType.LAZY) @JoinTable( name="user_likes_comment", joinColumns = @JoinColumn(name="user_id"), inverseJoinColumns = @JoinColumn(name="comment_id") ) private List<Comment> commentsUserLikes; @ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private List<Role> roles; }
Photo实体类
@Entity @Table(name="photos") public class Photo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String photoURL; private String photoFileName; @NotEmpty private String photoTitle; @NotEmpty private String photoDescription; //RELATIONSHIPS @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="user_id") private User user; @ManyToMany(fetch=FetchType.LAZY) @JoinTable( name="user_likes_photo", joinColumns = @JoinColumn(name="photo_id"), inverseJoinColumns = @JoinColumn(name="user_id") ) private List<User> usersWhoLikePhoto; @OneToMany(mappedBy="photo", fetch = FetchType.LAZY) private List<Comment> comments; //CREATED AND UPDATED AT @Column(updatable=false) @DateTimeFormat(pattern="yyyy-MM-dd") private Date createdAt; @DateTimeFormat(pattern="yyyy-MM-dd") private Date updatedAt; }
解决方案
服务器端的editPressed变量是全局状态,无法单独控制单条评论的编辑状态,因此必须用前端JS+AJAX实现局部无刷新编辑,Spring的form:form本质是生成HTML表单,JS可直接操作DOM完成状态切换。
1. 修改JSP代码,添加单条评论标识
给每条评论容器添加唯一ID标识,同时将静态评论内容和编辑表单放在同一容器内,默认隐藏编辑表单:
<c:forEach var="eachComment" items="${allCommentsByPhotoId}"> <div class="card-text d-flex gap-2 mb-4" data-comment-id="${eachComment.id}"> <img class="user-comment-card" src="https://cdn-icons-png.flaticon.com/512/1053/1053244.png" alt="" /> <div> <!-- 静态评论内容 --> <div class="comment-content mb-2"> <span class="username"><c:out value="${eachComment.user.firstName}"/></span> says: <span class="comment-text"><c:out value="${eachComment.comment}"/></span> </div> <!-- 编辑表单,默认隐藏 --> <div class="comment-edit-form mb-2 d-none"> <span class="username"><c:out value="${eachComment.user.firstName}"/></span>: <form class="edit-comment-form" action="/edit/comment/${eachComment.id}" method="post"> <input type="text" name="comment" class="form-control" value="${eachComment.comment}" /> <div class="mt-2 d-flex gap-2"> <button type="submit" class="btn btn-sm btn-primary">保存</button> <button type="button" class="btn btn-sm btn-secondary cancel-edit-btn">取消</button> </div> </form> </div> <div class="d-flex align-items-center gap-3"> <c:if test="${ eachComment.user.id == currentUser.id }"> <form action="/delete/${eachComment.id}" method="post"> <input type="hidden" name="_method" value="delete" /> <input class="btn btn-danger del-cmt-btn" type="submit" value="Delete" /> </form> <button class="btn btn-secondary edit-cmt-btn" type="button">Edit</button> </c:if> </div> </div> </div> </c:forEach>
注:Spring MVC默认不支持表单直接提交DELETE请求,需添加
_method隐藏字段,并在后端配置HiddenHttpMethodFilter。
2. 添加JS逻辑,实现状态切换与无刷新提交
在页面底部添加原生JS代码,处理编辑按钮点击、取消编辑、表单提交逻辑:
// 监听编辑/取消按钮点击 document.addEventListener('click', function(e) { if (e.target.classList.contains('edit-cmt-btn')) { const commentCard = e.target.closest('[data-comment-id]'); // 切换显示状态:隐藏静态内容,显示编辑表单 commentCard.querySelector('.comment-content').classList.add('d-none'); commentCard.querySelector('.comment-edit-form').classList.remove('d-none'); } else if (e.target.classList.contains('cancel-edit-btn')) { const commentCard = e.target.closest('[data-comment-id]'); // 取消编辑,恢复静态内容显示 commentCard.querySelector('.comment-content').classList.remove('d-none'); commentCard.querySelector('.comment-edit-form').classList.add('d-none'); } }); // 监听编辑表单提交,用AJAX实现无刷新更新 document.addEventListener('submit', function(e) { if (e.target.classList.contains('edit-comment-form')) { e.preventDefault(); // 阻止默认表单跳转 const form = e.target; const commentCard = form.closest('[data-comment-id]'); const commentText = commentCard.querySelector('.comment-text'); // 用Fetch API提交表单数据 fetch(form.action, { method: 'POST', body: new FormData(form), headers: { 'X-Requested-With': 'XMLHttpRequest' // 标记为AJAX请求 } }) .then(response => response.json()) .then(data => { // 更新页面上的评论文本 commentText.textContent = data.comment; // 切换回静态内容显示 commentCard.querySelector('.comment-content').classList.remove('d-none'); commentCard.querySelector('.comment-edit-form').classList.add('d-none'); }) .catch(error => console.error('编辑失败:', error)); } });
3. 调整后端Controller接口
修改评论编辑接口,支持返回JSON格式的更新后评论数据:
@PostMapping("/edit/comment/{id}") @ResponseBody // 标记返回JSON数据 public Comment updateComment(@PathVariable Long id, @RequestParam String comment, Principal principal) { // 获取当前登录用户 User currentUser = userService.findByUsername(principal.getName()); Comment commentToUpdate = commentService.findById(id); // 验证用户权限 if (commentToUpdate.getUser().getId().equals(currentUser.getId())) { commentToUpdate.setComment(comment); commentToUpdate.setUpdatedAt(new Date()); commentService.save(commentToUpdate); } return commentToUpdate; }
内容的提问来源于stack exchange,提问作者Alexander Zuniga
相关产品推荐
相关产品推荐

