Skip to content

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

I’m trying to make a simple Todo app to learn asp net core mvc.

I did the CRUD to manage the todos and it worked fine. For the next step i wanted to try adding Ajax to it (avoiding to reload the entire page), delete worked fine, create too, but when i want to edit one todo (which is basically a form) the response of the Ajax request sets all the inputs of all the todos at the same value.

If I update “Buy chocolat” by “Buy chocolate” as the title of one todo, all other todos will have a title “Buy chocolate”.

If I refresh the page (or just the section containing todos) everything goes back to normal, which means the database updated just the todo I wanted to.

It’s really weird and it probably comes from the fact that the inputs have the same name value (todo 1 title => todo.Title, todo 2 title => todo.Title, etc…) even though it works fine for all the rest.

Here’s the page with the container of todos :

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

<partial name="_DeleteModal">

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

Here’s the foreach that displays all todos which also is the partial view “_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="[email protected]" />
        <div class="todo-up todo-row">
            <textarea autocomplete="off" placeholder="Put the title here..." class="todo-header" asp-for="@todo.Title" id="[email protected]" ></textarea>
            <textarea autocomplete="off" placeholder="Put the description here..." class="todo-description" asp-for="@todo.Description" id="[email protected]" ></textarea>
        <div class="todo-down todo-row">
            <div class="todo-validation-row">
                <i class="fa-regular fa-check todo-edit" alt="Saved"></i>
                <span class="tooltip-text">Saved</span> @*Tooltip for edition*@
            <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>

The beginning of the controller method :

        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;
                        await _context.SaveChangesAsync();
                        var todos = GetTodosOfConnectedUser();
                        var partialView = PartialView("_TodoList", todos);
                        return partialView;

The GetTodosOfConnectedUser method (which return an Enumerable object of Todos that belongs to the user currently connected) :

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

And the JS for the Ajax request :

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

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

Thank you for your time


So, the problem is weird. Like, really weird.

I have followed what’s happening step-by-step and everything is going smoothly and then… All the forms get the same inputs/textareas for no apparent reasons.

I believe it comes from the fact that I create one form for each todo, which is a really bad practice, probably never meant to be done in the first place. If you ever encounter this problem, just change the way you do it.