Share via

How run C# Post method to delete a DB register on confirm to bootstrap modal in Razor Pages web app

Pablo The Tiger 186 Reputation points
2026-02-16T13:55:27.0233333+00:00

Hello (I'm new to the forum)

I'm working on an ASP.NET Core Razor Pages web app. It's a CRUD. In my Index.cshtml I have a list of products (as in the DB that is created with EF Core), and in each row after the fields I have an Edit and Delete buttons. I already managed to implement Create and Edit in the same Razor Page. What I want now is to be able to launch the bootstrap confirmation modal dialog, and when I click the Confirm button in it, to run the C# post method that deletes the register in the DB and reloads the Index page. I was able to launch the bootstrap modal (with some Javascript I took from the web) but when I click the Confirm button the C# method is not called (I set a breakpoint) and the app stops and mark in the line:

@foreach (var producto in Model.productos) { // here go the rows }

the object 'productos' as null. All these occurs in just one Razor Page, Index. If you see, the HTML and the Javascript, everything is connected to end calling the OnPostDeleteConfirm() method, so that it should work.

I attach the files

The C# (Index.cshtml.cs)

using CrudProductos.Data;
using CrudProductos.Modelos;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace CrudProductos.Pages.Productos
{
    public class IndexModel : PageModel
    {
        private readonly ApplicationDBContext _context;
        public IList<Producto> productos { get; set; }
        public IndexModel(ApplicationDBContext context)
        {
            _context = context;
        }
        public void OnGet()
        {
            productos = _context.Producto.ToList();
        }
        public IActionResult OnPostDeleteConfirm(int id)
        {
            Producto Producto = _context.Producto.Find(id);
            if (Producto == null)
            {
                return NotFound();
            }
            _context.Producto.Remove(Producto);
            _context.SaveChanges();
            return RedirectToPage("Index");
        }
    }
}

The HTML and Javascript (Index.cshtml)

@page
@model CrudProductos.Pages.Productos.IndexModel
@{
    ViewData["Title"] = "Lista de Productos";
}
<h1>Lista de Productos</h1>
<a class="btn btn-primary mb-3" asp-page="CrearEditar" asp-route-id ="0">Agregar Nuevo Producto</a>
<table class="table table-striped">
    <thead>
        <tr>
            <th>Id</th>
            <th>Nombre</th>
            <th>Precio</th>
            <th>Stock</th>
            <th>Fecha Creación</th>
            <th>Acciones</th>
        </tr>
    </thead>
    <tbody>
    @foreach (var producto in Model.productos)
    {
        <tr>
            <td>@producto.Id</td>
            <td>@producto.Nombre</td>
            <td>@producto.Precio.ToString("C")</td>
            <td>@producto.Stock</td>
            <td>@producto.FechaCreación</td>
            <td>
                <a class="btn btn-secondary btn-sm" asp-page="CrearEditar" asp-route-id="@producto.Id">Editar</a>
                <button type="button" class="btn btn-danger btn-sm" id="openModalBtn" value="@producto.Id">Borrar</button>
                <a class="btn btn-info btn-sm" asp-page="Detalle" asp-route-id="@producto.Id">Detalle</a>
            </td>
        </tr>
        }
    </tbody>
</table>
<!-- Modal -->
<div class="modal fade" id="confirmationModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h1 class="modal-title fs-5" id="staticBackdropLabel">Modal title</h1>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                ...
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
                <button type="button" class="btn btn-primary" id="confirmActionBtn">Borrar</button>
            </div>
        </div>
    </div>
</div>
<!-- A hidden form to submit the action -->
<form method="post" id="myForm" asp-page-handler="OnPostDeleteConfirm">
    <input type="hidden" name="paramName" id="paramNameInput" />
</form>
@section Scripts {
    <script>
        $(document).ready(function () {
            var confirmationModal = new bootstrap.Modal(document.getElementById('confirmationModal'));
            var confirmActionBtn = document.getElementById('confirmActionBtn');
            var myForm = document.getElementById('myForm');
            var paramNameInput = document.getElementById('paramNameInput');
            var deleteBtn = document.getElementById('openModalBtn');
            // Add event listener to the confirm button
            confirmActionBtn.addEventListener('click', function () {
                // Optional: set some data to be passed to the C# method
                paramNameInput.value = document.getElementById('openModalBtn').value;
                // Submit the hidden form, which triggers the server-side method
                myForm.submit();
                // Hide the modal after submission (optional, as the page may reload)
                confirmationModal.hide();
            });
            deleteBtn.addEventListener('click', function() {
                confirmationModal.show();
            });
        });
    </script>
}

I will appreciate your help very much, Thanks

Pablo

Developer technologies | ASP.NET | ASP.NET Core
0 comments No comments
{count} votes

Answer accepted by question author
  1. Jack Dang (WICLOUD CORPORATION) 14,955 Reputation points Microsoft External Staff Moderator
    2026-02-17T04:18:57.9966667+00:00

    Hi @Pablo The Tiger ,

    Thanks for reaching out.

    The overall structure of your page is fine. It seems like the issue is in how the POST handler is being wired up and how the product ID is being passed back to the server.

    First, regarding the handler name: in Razor Pages, when you define a method like:

    public IActionResult OnPostDeleteConfirm(int id)
    

    the correct value for the form attribute is:

    asp-page-handler="DeleteConfirm"
    

    You should not include the OnPost prefix in the asp-page-handler. Razor automatically maps OnPostDeleteConfirm to DeleteConfirm. If the handler name doesn’t match correctly, the method will never be hit, which explains why your breakpoint is not triggered.

    Next, the hidden input field needs to match the method parameter name exactly. Your handler expects an int id, so your hidden field must be:

    <input type="hidden" name="id" id="idInput" />
    

    If the name is different (for example paramName), model binding won’t map the value to the id parameter.

    Another important point is the use of id="openModalBtn" inside your foreach loop. Since this button is rendered once per row, you are generating multiple elements with the same ID. IDs must be unique, and currently your JavaScript will only attach to the first button found. Replacing the ID with a class and attaching the event handler using querySelectorAll ensures the correct product ID is captured before submitting the form.

    Finally, regarding Model.productos becoming null: this usually happens when the handler is not executed and the page re-renders without going through a proper redirect. Once the handler runs and you return:

    return RedirectToPage("Index");
    

    a fresh GET request will populate productos again via OnGet().

    If the problem persists after these changes, you may need to review how the scripts are loaded or whether the form submission is being blocked by another client-side error.

    Hope this helps! If my answer was helpful - kindly follow the instructions here so others with the same problem can benefit as well.

    2 people found this answer helpful.

Answer accepted by question author
  1. SurferOnWww 5,581 Reputation points
    2026-02-18T00:57:33.24+00:00

    now the question is ¿how do I pass the correct id to the C# method, from the correct Delete button?

    Please consider the followings:

    (1) Write javascript method which opens the Modal. The method must have the argument to which the value of id is passed. Write the value of argument to the value of hidden field.

    (2) Set the javascript method to the [Delete] link. Pass the value of id to its argument.

    Below is working sample:

    .cshtml.cs

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    using Microsoft.EntityFrameworkCore;
    using RazorPages.Models;
    
    namespace RazorPages.Pages.Movies
    {
        public class IndexModel : PageModel
        {
            private readonly RazorPages.Data.TestDatabaseContext _context;
    
            public IndexModel(RazorPages.Data.TestDatabaseContext context)
            {
                _context = context;
            }
    
            public IList<Movie> Movie { get; set; } = default!;
    
            public async Task OnGetAsync()
            {
                Movie = await _context.Movies.ToListAsync();
            }
    
            public async Task<IActionResult> OnPostDeleteConfirm(int? id)
            {
                if (id == null)
                {
                    return NotFound();
                }
    
                var movie = await _context.Movies.FindAsync(id);
                if (movie != null)
                {                
                    _context.Movies.Remove(movie);
                    await _context.SaveChangesAsync();
                }
    
                return RedirectToPage("./Index");
            }
        }
    }
    

    .cshtml

    @page
    @model RazorPages.Pages.Movies.IndexModel
    
    @{
        ViewData["Title"] = "Index";
    }
    
    <h1>Index</h1>
    
    <p>
        <a asp-page="Create">Create New</a>
    </p>
    <table class="table">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.Movie[0].Title)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Movie[0].ReleaseDate)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Movie[0].Genre)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Movie[0].Price)
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
    @foreach (var item in Model.Movie) {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ReleaseDate)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Genre)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </td>
                <td>
                    <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
                    <a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
                    @* <a asp-page="./Delete" asp-route-id="@item.Id">Delete</a> *@
                    <a href="javascript:void(0);" onclick="javascript:openModal('@item.Id')">Delete</a>
                </td>
            </tr>
    }
        </tbody>
    </table>
    <!-- Modal -->
    <div class="modal fade" id="confirmationModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h1 class="modal-title fs-5" id="staticBackdropLabel">Modal title</h1>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    ...
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
                    <button type="button" class="btn btn-primary" id="confirmActionBtn">Borrar</button>
                </div>
            </div>
        </div>
    </div>
    
    <!-- A hidden form to submit the action -->
    <form method="post" id="myForm" asp-page-handler="DeleteConfirm">
        <input type="hidden" name="id" id="paramNameInput" />
    </form>
    
    @section Scripts {
        <script>
            $(document).ready(function () {
                var confirmationModal = new bootstrap.Modal(document.getElementById('confirmationModal'));
                var confirmActionBtn = document.getElementById('confirmActionBtn');
                var myForm = document.getElementById('myForm');
                var paramNameInput = document.getElementById('paramNameInput');
    
                window.openModal = function (id) {
                    // Set the value of the hidden input to the id
                    paramNameInput.value = id;
                    // Show the modal
                    confirmationModal.show();
                }
    
                confirmActionBtn.addEventListener('click', function () {
                    // Submit the form when the confirm button is clicked
                    myForm.submit();
                });
            });
        </script>
    }
    

    id is passed

    enter image description here

    0 comments No comments

Answer accepted by question author
  1. Marcin Policht 82,760 Reputation points MVP Volunteer Moderator
    2026-02-16T14:41:42.7166667+00:00

    Here are a few things to try:

    1. Set the Button's ID on Modal Opening: In the JavaScript part, you're attaching the event to the delete button using the id="openModalBtn". However, since you're iterating over products, this will only attach the event listener to the last openModalBtn in the list (because id attributes should be unique). This is likely causing the issue. Instead, you should target all the delete buttons dynamically using a class, and within the click handler, get the product's ID from the button's value attribute.
    2. Pass the Product ID: When the delete button is clicked, the modal should reflect the correct product ID. You can achieve this by setting the value of the hidden input field (paramNameInput) when the modal is shown.

    JavaScript Fix:

    @section Scripts {
    

    HTML Fix:

    <button type="button" class="btn btn-danger btn-sm" value="@producto.Id">Borrar</button>
    

    C# (Backend):

    Ensure that your OnPostDeleteConfirm method matches the name of the parameter (paramNameInput). The parameter in your C# method should match the form data that is being sent.

    public IActionResult OnPostDeleteConfirm(int paramName)
    


    If the above response helps answer your question, remember to "Accept Answer" so that others in the community facing similar issues can easily find the solution. Your contribution is highly appreciated.

    hth

    Marcin


1 additional answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 83,661 Reputation points Volunteer Moderator
    2026-02-16T16:51:54.8733333+00:00

    You have two issues.

    • As suggested the delete button ids are not unique, so only the first button will be hooked up.
    • the OnPostDeleteConfirm(int id) method require a query string parameter named id which you did not supply. Normally you would use asp-route-id on the form submit button to specify the value, but you are not using a form submit button. So remove the parameter from the post action and use the hidden field paramName instead. You will need to add a matching model property

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.