【问题标题】:When one form is updated (using Ajax) reloading the forms make them have the same values as the previously updated one当更新一个表单(使用 Ajax)时,重新加载表单使它们具有与先前更新的相同的值
【发布时间】: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" 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 方法(返回属于当前连接的用户的 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);外面去调试看看返回值是什么

标签: javascript jquery ajax asp.net-core-mvc asp.net-ajax


【解决方案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" 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);
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-29
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多