当更新一个表单时(使用 Ajax)重新加载表单使它们具有与先前更新的相同的值

When one form is updated (using Ajax) reloading the forms make them have the same values as the previously updated one

我正在尝试制作一个简单的 Todo 应用来学习 asp net core mvc。

我做了 CRUD 来管理待办事项,它运行良好。对于下一步,我想尝试向其中添加 Ajax (避免重新加载整个页面),删除效果很好,也可以创建,但是当我想编辑一个待办事项(基本上是一种形式)时,响应Ajax 请求将所有待办事项的所有输入设置为相同的值。

如果我将“购买巧克力”更新为一个待办事项的标题,则所有其他待办事项的标题都将是“购买巧克力”。

如果我刷新页面(或仅包含待办事项的部分),一切都会恢复正常,这意味着数据库只更新了我想要的待办事项。

这真的很奇怪,这可能是因为输入具有相同的名称值(todo 1 title => todo.Title、todo 2 title => todo.Title,等等... ) 尽管它对所有其他人都很好。

这是包含待办事项容器的页面:

@model IEnumerable<TodoApp.Models.Todo>

@section Css{
    <link href="/css/todos.css" rel="stylesheet" />
    <link href="~/lib/fontawesome/css/all.css" rel="stylesheet" />
}

@{
    ViewData["Title"] = "List of todos";
}

<h1>My list of Todos</h1>

<span class="error-span" style="color:red"></span>

<div id="main_container">
    
    <i onclick="createTodo()" id="create-button" class="fas fa-plus-circle" title="Add new todo"></i>

    <div id="todos_container">
        @await Html.PartialAsync("_TodoList", Model)
    </div>
</div>

<partial name="_DeleteModal">

@section Scripts{
    <script src="~/js/todos.js"></script>
}

这是显示所有待办事项的 foreach,它也是部分视图“_TodoList”:

@model IEnumerable<TodoApp.Models.Todo>

@foreach (Todo todo in Model)
{
    <form class="todo" asp-action="Edit" asp-controller="Todos" data-id="@todo.Id">
        <input type="hidden" asp-for="@todo.Id" id="id_@todo.Id" />
        <div class="todo-up todo-row">
            <textarea autocomplete="off" placeholder="Put the title here..." class="todo-header" asp-for="@todo.Title" id="title_@todo.Id" ></textarea>
            <textarea autocomplete="off" placeholder="Put the description here..." class="todo-description" asp-for="@todo.Description" id="decription_@todo.Id" ></textarea>
        </div>
        <div class="todo-down todo-row">
            <div class="todo-validation-row">
                <span></span>
                <i class="fa-regular fa-check todo-edit" alt="Saved"></i>
                <span class="tooltip-text">Saved</span> @*Tooltip for edition*@
            </div>
            <div class="todo-footer">
                <div class="todo-updated"><img src="~/assets/img/update.svg" alt="Updated at" /><span>@todo.UpdatedDate</span></div>
                <a onclick="showDeleteModal(@todo.Id)" title="Delete todo">
                    <i class="fas fa-trash"></i>
                </a>
            </div>
        </div>
    </form>
}

控制器方法的开头:

[HttpPatch]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit([Bind("Id", "Title", "Description")] Todo todo)
        {
            if (ModelState.IsValid)
            {
                var matchingTodo = await _context.Todos.FindAsync(todo.Id);
                if (matchingTodo != null)
                {
                    if (GetConnectedUserId() == matchingTodo.UserId)
                    {
                        matchingTodo.Title = todo.Title;
                        matchingTodo.Description = todo.Description;
                        matchingTodo.UpdatedDate = DateTime.Now;
                        _context.Update(matchingTodo);
                        await _context.SaveChangesAsync();
                        var todos = GetTodosOfConnectedUser();
                        var partialView = PartialView("_TodoList", todos);
                        return partialView;

GetTodosOfConnectedUser 方法(return Todos 的可枚举 object 属于当前连接的用户):

private IEnumerable<Todo> GetTodosOfConnectedUser()
{
            return _context.Todos.Where(t => t.UserId == Convert.ToInt32(HttpContext.User.FindFirst("user_id").Value)).OrderByDescending(t => t.UpdatedDate);
}

以及 Ajax 请求的 JS:


${'.todo'}.on("change", function (ev) {
        let form = ev.currentTarget;
        editTodo(form);
    });

function editTodo(form) {
    try {
        $.ajax({
            type: 'PATCH',
            url: form.action,
            data: new FormData(form),
            processData: false,
            contentType: false,
            success: function (res) {
                $(".error-span").html("");
                $('#todos_container').html(res);
            },
            error: function (err) {
                console.log(err);
                $(".error-span").html("An error occured please try again.");
            }
        });
        return false;
    }
    catch (ex) {
        console.log(ex);
    }
}

感谢您的宝贵时间

所以,这个问题很奇怪。就像,真的很奇怪。

我已经一步步跟踪发生的事情,一切进展顺利,然后...所有表格都变得相同 inputs/textareas,没有明显的原因。

我相信这是因为我为每个待办事项创建了一个表单,这是一种非常糟糕的做法,可能一开始就不想这样做。如果您曾经遇到过这个问题,只需改变您的处理方式即可。

我展示了我的代码,希望它能帮到你:

型号

    public class Todo
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string UpdatedDate { get; set; }
    }

查看

@model IEnumerable<TodoApp.Models.Todo>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<div id="todos_container">
    @foreach (Todo todo in Model)
{
    <form class="todo" asp-action="Edit" asp-controller="Todos" data-id="@todo.Id">
        <input type="hidden" asp-for="@todo.Id" id="id_@todo.Id" />
        <div class="todo-up todo-row">
            <textarea autocomplete="off" placeholder="Put the title here..." class="todo-header" asp-for="@todo.Title" id="title_@todo.Id" ></textarea>
            <textarea autocomplete="off" placeholder="Put the description here..." class="todo-description" asp-for="@todo.Description" id="decription_@todo.Id" ></textarea>
        </div>
        <div class="todo-down todo-row">
            <div class="todo-validation-row">
                <span></span>
                <i class="fa-regular fa-check todo-edit" alt="Saved"></i>
                <span class="tooltip-text">Saved</span> @*Tooltip for edition*@
            </div>
            <div class="todo-footer">
                <div class="todo-updated"><img src="~/assets/img/update.svg" alt="Updated at" /><span>@todo.UpdatedDate</span></div>
                <a onclick="showDeleteModal(@todo.Id)" title="Delete todo">
                    <i class="fas fa-trash"></i>
                </a>
            </div>
        </div>
    </form>
}
</div>

@*@section Scripts {*@
    <script>
        $('.todo').on("change", function (ev) {
           
            let form = ev.currentTarget;
            editTodo(form);
        });

    function editTodo(form) {
        try {
            $.ajax({
                type: 'PATCH',
                url: '/todoes/Edit',
                data: new FormData(form),
                processData: false,
                contentType: false,
                success: function (res) {
                    $(".error-span").html("");
                    $('#todos_container').html(res);
                },
                error: function (err) {
                    console.log(err);
                    $(".error-span").html("An error occured please try again.");
                }
            });
            return false;
        }
        catch (ex) {
            console.log(ex);
        }
    }
    </script>
@*}*@

动作

        [HttpPatch]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit([Bind("Id", "Title", "Description")] Todo todo)
        {
            if (ModelState.IsValid)
            {
                var matchingTodo = await _context.Todos.FindAsync(todo.Id);
                if (matchingTodo != null)
                {
                        matchingTodo.Title = todo.Title;
                        matchingTodo.Description = todo.Description;
                        matchingTodo.UpdatedDate = DateTime.Now.ToString();
                        _context.Update(matchingTodo);
                        await _context.SaveChangesAsync();
                        var todos = GetTodosOfConnectedUser();
                        var partialView = PartialView("_TodoList", todos);
                        return partialView;
                    
                }
                return BadRequest();
            }
            return View(todo);
        }