Search code examples
htmljqueryasp.net-core-mvcbootstrap-modal

ASP Net Core MVC Refresh Partial View following Add/Edit via Jquery PopUp Modal


I've been trying to work this out for the past few days and I wondered if anyone might be able to get me over the line with it.

The problem I have is that I want to add a new value to the database and then update the partial view without updating the main view which is just a dropdown list and button.

The code I have will currently create the new entry but then it shows the Index/List as its own view, rather than just an updated partial view.

Below is a Main View which a Partial View is rendered to following a button click which renders a partial view based on a dropdown value


@{
    ViewData["Title"] = "Manage DropDown Lists";
    //Layout = "~/Views/Shared/_Layout.cshtml";
}

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">

<h1>Manage Lists</h1>

<p>
    Select List:
    @Html.DropDownList("ddl", ViewData["selected"] as List<SelectListItem>)
    <button id="btn-ddl" data-url='@Url.Action("Index", "param")' class="js-load-partial; btn btn-primary btn-sm">Get List</button>
</p>
<hr />
<br />

<div id="detailsDiv">

</div>

@section Scripts {
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js" integrity="sha512-aVKKRRi/Q/YV+4mjoKBsE4x3H+BkegoM/em46NNlCqNTmUYADjBbeNefNxYV7giUp0VxICtqdrbqU7iVaeZNXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="~/js/site.js"></script>
}

This is the Jquery for the Button Click (btn-ddl)

//Render PartialView from Button click - input Id from DropDownList - Manage Lists
$(document).ready(function () {
    $("body").on("click", '#btn-ddl', function (evt) {
        var $detailDiv = $('#detailsDiv'),
            url = $(this).data('url');

        url = url.replace("param", $("#ddl").val());

        $.get(url, function (data) {
            $detailDiv.html(data);
        });
    });
});

This is one of the Partial Views which is rendered. This partialview is an Index of the selected model. It also contains a button to Add a new item to the list, and edit buttons to edit the items in the list.

@model IEnumerable<PoCStaffDatabase.Models.AccessLevel>

<div>
    <h1>Access Levels</h1>
    <br />
    <hr />
</div>

<div id="PlaceHolderHere">
    <!-- Modal Popup Placeholder -->
</div>

<button type="button" class="btn btn-primary" data-toggle="ajax-modal" data-target="#addAccessLevel" data-url="@Url.Action("Create")" id="showModalPopup">
    Create
</button>

<br /><br />

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.AccessLevelName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Live)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.AccessLevelName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Live)
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.AccessLevelId">Edit</a> 
                </td>
            </tr>
        }
    </tbody>
</table>


The 'Create' button (id="showModalPopup") is clicked to create a new item, and this is done via a Partial View (below) which appears as a Modal Popup.

@model PoCStaffDatabase.Models.AccessLevel

<div class="modal fade" id="addAccessLevel" tabindex="-1" role="dialog" aria-labelledby="addAccessLevelLabel" aria-hidden="true">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="addAccessLevelLabel">Add Access Level</h5>
                <button type="button" class="close" data-dismiss="modal" id="btnClose" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>

            <div class="modal-body">
                <form asp-action="Create">
                    <div class="form-group">
                        <label asp-for="AccessLevelName" class="control-label"></label>
                        <input asp-for="AccessLevelName" class="form-control" />
                        <span asp-validation-for="AccessLevelName" class="text-danger"></span>
                    </div>
                    <div class="form-group form-check">
                        <label class="form-check-label">
                            <input class="form-check-input" asp-for="Live" /> @Html.DisplayNameFor(model => model.Live)
                        </label>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-danger" data-dismiss="modal" id="btnClose" >Cancel</button>
                        
                        <button type="submit" class="btn btn-primary" data-save="modal" data-url='@Url.Action("Index", "AccessLevels")'id="btnSave">Create New</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div> 

This is the Jquery code which runs from the 'Create New' button (id="btnSave"). I have left in the last thing I tried in order to update the partial view but it's commented out.

Again even with that part in the code the partial view renders as a full view (without formatting as the _Layout view isn't shown either.

$(document).ready(function () {
    $('body').on("click", '#showModalPopup', function (event) {

        var PlaceHolderElement = $('#PlaceHolderHere'),
            url = $(this).data('url');

        PlaceHolderElement.empty();

        $.get(url, function (data) {
            PlaceHolderElement.html(data);
            PlaceHolderElement.find('.modal').modal('show');
        });
    });

    PlaceHolderElement.on("click", '#btnSave', function (event) {
        form = $(this).parents('.modal').find('form'),
            actionUrl = form.attr('action'),
            sendData = form.serialize();

        $.post(actionUrl, sendData).done(function (data) {
            PlaceHolderElement.find('.modal').modal('hide');
        });

       // var $detailDiv = ('#detailsDiv'),
       //         url = $(this).data('url');
       //
       //     $.get(url, function (data) {
       //         $detailDiv.html(data);
       //     });

    });
});

Finally this is the Controller for the Create and Index actions.

namespace PoCStaffDatabase.Controllers
{
    public class AccessLevelsController : Controller
    {
        private readonly AppDbContext _context;

        public AccessLevelsController(AppDbContext context)
        {
            _context = context;
        }

        // GET: AccessLevels
        public async Task<IActionResult> Index()
        {
            var accesslevel = await _context.AccessLevel.ToListAsync();

            return PartialView("_partialIndex", accesslevel);
            //return View(await _context.AccessLevel.ToListAsync());
        }

        // GET: AccessLevels/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var accessLevel = await _context.AccessLevel
                .FirstOrDefaultAsync(m => m.AccessLevelId == id);
            if (accessLevel == null)
            {
                return NotFound();
            }

            return View(accessLevel);
        }

        // GET: AccessLevels/Create
        public IActionResult Create()
        {
            AccessLevel accesslevel = new AccessLevel();

            return PartialView("_partialCreate", accesslevel);
        }

        // POST: AccessLevels/Create
        // To protect from overposting attacks, enable the specific properties you want to bind to, for 
        // more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("AccessLevelId,AccessLevelName,Live")] AccessLevel accessLevel)
        {
            if (ModelState.IsValid)
            {
                _context.Add(accessLevel);
                await _context.SaveChangesAsync();
                //return NoContent();
                return RedirectToAction("Index");

                //return PartialView("_partialIndex", await _context.AccessLevel.ToListAsync() );
            }
            return View(accessLevel);
        }

Any help that anyone can give would be greatly appreciated.

Thanks.


Solution

  • You need change the js like below:

    $(document).ready(function () {
        $('body').on("click", '#showModalPopup', function (event) {
            var PlaceHolderElement = $('#PlaceHolderHere'),
                url = $(this).data('url');
    
            PlaceHolderElement.empty();
    
            $.get(url, function (data) {
                PlaceHolderElement.html(data);
                var data = PlaceHolderElement.find('.modal');
                //modify here......
                $(PlaceHolderElement.find('.modal')).modal('show')
            });
            //});     remove here
    
            PlaceHolderElement.on("click", '#btnSave', function (event) {
                form = $(this).parents('.modal').find('form'),
                    actionUrl = form.attr('action'),
                    sendData = form.serialize();
    
                $.post(actionUrl, sendData).done(function (data) {
                    //modify here......
                    $(PlaceHolderElement.find('.modal')).modal('hide');
                    $('#detailsDiv').html(data);  //add this...
                });
            });
        });
    }) //add this...
    

    Modify your _partialCreate here:

    @model AccessLevel
           @*remove fade*@
    <div class="modal" id="addAccessLevel" tabindex="-1" role="dialog" aria-labelledby="addAccessLevelLabel" aria-hidden="true">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="addAccessLevelLabel">Add Access Level</h5>
                    <button type="button" class="close" data-dismiss="modal" id="btnClose" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
    
                <div class="modal-body">
                    <form asp-action="Create">
                        <div class="form-group">
                            <label asp-for="AccessLevelName" class="control-label"></label>
                            <input asp-for="AccessLevelName" class="form-control" />
                            <span asp-validation-for="AccessLevelName" class="text-danger"></span>
                        </div>
                        <div class="form-group form-check">
                            <label class="form-check-label">
                                <input class="form-check-input" asp-for="Live" /> @Html.DisplayNameFor(model => model.Live)
                            </label>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-danger" data-dismiss="modal" id="btnClose">Cancel</button>
                              @*change here.........*@
                            <button type="button" class="btn btn-primary" data-save="modal" data-url='@Url.Action("Index", "AccessLevels")' id="btnSave">Create New</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div> 
    

    Backend code:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("AccessLevelId,AccessLevelName,Live")] AccessLevel accessLevel)
    {   
        if (ModelState.IsValid)
        {
            _context.Add(accessLevel);
            await _context.SaveChangesAsync();
            return PartialView("_partialIndex", await _context.AccessLevel.ToListAsync() ); 
        } 
        throw new Exception("xxxxx");  //ajax cannot judge the modelstate error
    }