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

Posted

技术标签:

【中文标题】当更新一个表单(使用 Ajax)时,重新加载表单使它们具有与先前更新的相同的值【英文标题】:When one form is updated (using Ajax) reloading the forms make them have the same values as the previously updated one 【发布时间】:2021-12-13 16:18:12 【问题描述】:

我正在尝试制作一个简单的 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" ></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"  /><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 方法(返回属于当前连接的用户的 Todos 的 Enumerable 对象):

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);
    

感谢您的宝贵时间

【问题讨论】:

我在我的 vs 中尝试了你的代码,它工作正常,你可以尝试另一个浏览器并显示更多细节。 遗憾的是,它在 Firefox 中也不起作用:/ 你能提供你的HomePage,PartialViewGetTodosOfConnectedUser()吗?顺便问一下,$('#todos_container').html(res); 是什么?我没有看到任何id=todos_container 我正在编辑我的帖子 :) 我没有提供这些信息,因为我真的不认为它会改变任何东西,问题可能来自名称属性的“重复”但如果它可以帮忙,我会发布它们 嗨,GetTodosOfConnectedUser() 中的 UserId 是什么?建议你把这行代码移到_context.Todos.Where(t =&gt; t.UserId == Convert.ToInt32(HttpContext.User.FindFirst("user_id").Value)).OrderByDescending(t =&gt; t.UpdatedDate);外面去调试看看返回值是什么 【参考方案1】:

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

我一步一步地跟踪正在发生的事情,一切进展顺利,然后......所有表单都获得相同的输入/文本区域,没有明显的原因。

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

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。【参考方案2】:

我展示了我的代码,希望它可以帮助你:

型号

    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" ></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"  /><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);
        

【讨论】:

以上是关于当更新一个表单(使用 Ajax)时,重新加载表单使它们具有与先前更新的相同的值的主要内容,如果未能解决你的问题,请参考以下文章

在不重新加载页面的情况下更新 Django 中的表单值?

在烧瓶中提交后重新加载表单

ajax iframe重新加载后图像不显示

提交时 ASP.NET jQuery 重新加载元素(Ajax POST/GET)

使用 ajax 和 mvc 提交带有照片数据的表单

如何通过ajax从表单发送POST数据数组?