You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 19:35:24