Info Modal after Confirmation Modal in Razor Pages

Viewed 3021

I want to show an info modal that says "Record successfully deleted." after clicking the button inside a Confirmation Modal.

Here is my code to show the confirmation modal

Controller

public IActionResult Delete()
{
   return PartialView("_ModalDelete");
}

_ModalDelete.cshtml

@using Data.ViewModels.Modal

@using (Html.BeginForm())
{
    @await Html.PartialAsync("_ModalHeader", new ModalHeader { Heading = "Delete" })

    <div class="modal-body form-horizontal">
        Are you sure you want to delete this record?
    </div>

    @await Html.PartialAsync("_ModalFooter", new ModalFooter { SubmitButtonText = "Delete" })
}

Example Screenshot:

enter image description here

This seems to be okay on this part. No issues encounter. But after clicking the Delete button, it will show my modal like a whole view. See below:

enter image description here

Here is my code:

Controller - for post of data after clicking delete button

[HttpPost]
public async Task<IActionResult> Delete(int id)
{
    try
    {
        var validationResult = await new RegionHandler(_regionService).CanDelete(id);
        if (validationResult == null)
        {
            await _regionService.DeleteById(id);

            return PartialView("_ModalInfo", new Tuple<string, string>(Constants.Message.Info, Constants.Message.RecordSuccessDelete));
        }

        ModelState.AddModelError(validationResult);
    }
    catch (Exception ex)
    {
        var exceptionMessage = await Helpers.GetErrors(ex, _emailService);
        ModelState.AddModelError(new ValidationResult(exceptionMessage));
    }

    ModelState.AddModelError(string.Empty, "Invalid delete attempt.");

    return PartialView("_ModalInfo", new Tuple<string, string>(Constants.Message.Error, ModelState.ToString()));
}

_ModalInfo.cshtml

@using Data.ViewModels.Modal
@model Tuple<string,string>

@await Html.PartialAsync("_ModalHeader", new ModalHeader { Heading = Model.Item1})

<div class="modal-body form-horizontal">
    @Model.Item2
</div>

@await Html.PartialAsync("_ModalFooter", new ModalFooter { CancelButtonText = "OK", OnlyCancelButton = true})
2 Answers

With the submission of your form you are making a roundtrip to the server, which will issue a completely new html page (even if your html code is only partial).

To remove the question-modal and replace it with a message-modal in the original page (region-list), you will have to use javascript (for the post AND the replacement).

If you want to stick with the roundtrip, make the Delete method return a full html page, which integrates the message-dialog (like the region-list intergrates the question-dialog).

Finally found an answer with this. So basically I just revised everything so that the model validation from controller will still be there.

Heres my code:

For the table markup

<tr>
    <td>
        @Html.DisplayFor(modelItem => item.RegionName)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.RegionCode)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.RegionKey)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Description)
    </td>
    <td class="text-center">
        <a asp-action="Edit" asp-route-id="@item.RegionId"><i class="fa fa-edit text-info"></i></a>
        <a href="#" onclick="showDeleteConfirmation('@string.Format(Constants.Message.DeletePrompt, item.RegionName)', event, @item.RegionId)"><i class="fa fa-trash text-danger"></i></a>
    </td>
</tr>

where it call a javascript function below:

@section Scripts{
    <script type="text/javascript">
        function showDeleteConfirmation(message, event, id) {
            event.preventDefault();

            showConfirmationModal(message).then(function () {
                $("#id").val(id);
                $("#formDelete").submit();
            });
        }
    </script>
}

where showConfirmationModal() is a promise function that uses bootbox.js (library that wraps bootstrap modal for easier usage).

site.js

function showConfirmationModal(message, title = "Confirm", size = "medium", confirmText = "Yes", canceltext = "No") {
    const deffered = $.Deferred();

    bootbox.confirm({
        title: title,
        message: message,
        size: size,
        buttons: {
            confirm: {
                label: confirmText,
                className: "btn-success"
            },
            cancel: {
                label: canceltext,
                className: "btn-danger"
            }
        },
        callback: function (result) {
            if (result) {
                deffered.resolve(result);
            } else {
                deffered.reject(result);
            }
        }
    });

    return deffered.promise();
}

On callback, it will submit the hidden form below. Ofcourse don't forget to set the id to be deleted.

Hidden form for Delete action

<form method="post" asp-action="Delete" id="formDelete" class="hidden">
    <input type="hidden" id="id" name="id" />
    <input type="hidden" asp-for="Item1.RegionName" name="RegionName" />
    <input type="hidden" asp-for="Item1.Page" name="Page" />
    <input type="hidden" asp-for="Item1.SortBy" name="SortBy" />
    <input type="hidden" asp-for="Item1.SortOrder" name="SortOrder" />
</form>

To show the Info Message(for success delete), I created a PartialView to make the modal show if theres a data set in a Temporary Data or TempData. This was added under the _Layout.cshtml page:

_ModalScriptsInit.cshtml

@using Data.Utilities
@{
    var text = TempData[Constants.Common.ModalMessage];
    if (text != null && !text.Equals(string.Empty))
    {
        <script type="text/javascript">
            showDefaultModal("@text");
        </script>
    }
}

So in my controller once successfully delete I will just set the TempData with its key as shown below:

Controller

[HttpPost]
public async Task<IActionResult> Delete(int id, RegionSearchViewModel searchViewModel)
{
    try
    {
        var validationResult = await new RegionHandler(_regionService).CanDelete(id);
        if (validationResult == null)
        {
            await _regionService.DeleteById(id);
            TempData[Constants.Common.ModalMessage] = Constants.Message.RecordSuccessDelete;

            return RedirectToAction(nameof(List), searchViewModel);
        }

        ModelState.AddModelError(validationResult);
    }
    catch (Exception ex)
    {
        var exceptionMessage = await Helpers.GetErrors(ex, _emailService);
        ModelState.AddModelError(new ValidationResult(exceptionMessage));
    }

    ModelState.AddModelError(string.Empty, "Invalid delete attempt.");

    return RedirectToAction(nameof(List), searchViewModel);
}

I am not sure yet if this is the best way so far. Please give suggestion on how to improve this code. Thanks!

Related