ASP.NET Core 2.2 - Details Modal With JSON Entity


Ken Haggerty
Created 08/19/2019 - Updated 08/25/2019 16:02

This article will demonstrate a call to a page handler which returns entity details in JSON. The details are displayed on a Bootstrap modal. I will assume you have created a new ASP.NET Core 2.2 Razor Pages project. I won't use Identity or Individual User Accounts.

The research project I used for the articles about table functions is published to tables.kenhaggerty.com. I created a topic, ASP.NET Core 2.2 - Table Functions Project for discussions. Access to the source code may be purchased at Manage > Assets.

If we create a page handler, we can use a Bootstrap modal rather than redirect to a page.

Edit Index.cshtml.cs > IndexModel, add the page handler:
public async Task<JsonResult> OnGetDetailsByIdAsync(int id = 0)
{
    if (id == 0)
    {
        return new JsonResult(new { Status = "Failed", Errors = "id = 0" });
    }

    var foodDetails = await _context.Foods
        .Where(f => f.Id == id)
        .Select(f => new Food
        {
            Id = f.Id,
            Name = f.Name,
            FoodType = f.FoodType,
            ColdStore = f.ColdStore,
            Date = f.Date
        })
        .FirstOrDefaultAsync();

    if (foodDetails == null)
    {
        return new JsonResult(new { Status = "Failed", Errors = "Details Not Found" });
    }

    return new JsonResult(new { Status = "Success", foodDetails });
}
Edit Index.cshtml, add the modal:
<div class="modal">
    <div class="modal-dialog">
        <div class="modal-content">

            <!-- Modal Header -->
            <div class="modal-header">
                <h4 class="modal-title">Food</h4>
                <button type="button" class="close" data-dismiss="modal">×</button>
            </div>

            <!-- Modal body -->
            <div class="modal-body">
            </div>

            <!-- Modal footer -->
            <div class="modal-footer">
                <button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
            </div>

        </div>
    </div>
</div>
Edit Index.cshtml, update the details link to a modal button:
<button type="button" class="btn btn-link p-0 details" data-id="@@item.Id"
    data-toggle="modal" data-target=".modal">Details</button>
Edit Index.cshtml, add the JavaScript:
@@section scripts {
        <script>

        const modalBody = document.querySelector('.modal-body');

        function getDetailsById(id) {

            return fetch('/index?handler=DetailsById&id=' + id,
                {
                    method: 'get',
                    headers: {
                        'Content-Type': 'application/json;charset=UTF-8'
                    }
                })
                .then(function (response) {
                    if (response.ok) {
                        return response.text();
                    } else {
                        throw Error('DetailsById Response Not OK');
                    }
                })
                .then(function (text) {
                    try {
                        return JSON.parse(text);
                    } catch (err) {
                        throw Error('DetailsById Method Not Found');
                    }
                })
                .then(function (responseJSON) {
                    modalBody.innerHTML = '';
                    if (responseJSON.status === 'Success') {
                        var foodDetails = responseJSON.foodDetails;
                        var dl = document.createElement('dl');
                        for (prop in foodDetails) {
                            var dt = document.createElement('dt');
                            dt.textContent = prop;
                            dl.appendChild(dt);
                            var dd = document.createElement('dd');
                            dd.textContent = foodDetails[prop].length === 0 ? 'empty' : foodDetails[prop];
                            dl.appendChild(dd);
                        }
                        var successDiv = document.createElement('div');
                        successDiv.classList.add('alert', 'alert-success');
                        successDiv.appendChild(dl);
                        modalBody.appendChild(successDiv);
                    }
                    else {
                        var dl = document.createElement('dl');
                        for (prop in responseJSON) {
                            var dt = document.createElement('dt');
                            dt.textContent = prop;
                            dl.appendChild(dt);
                            var dd = document.createElement('dd');
                            dd.textContent = responseJSON[prop];
                            dl.appendChild(dd);
                        }
                        var alertDiv = document.createElement('div');
                        alertDiv.classList.add('alert', 'alert-danger');
                        alertDiv.appendChild(dl);
                        modalBody.appendChild(alertDiv);
                    }
                })
                .catch(function (error) {
                    modalBody.innerHTML = '';
                    var alertDiv = document.createElement('div');
                    alertDiv.classList.add('alert', 'alert-danger');
                    alertDiv.textContent = 'Error ' + error;
                    modalBody.appendChild(alertDiv);
                });
        }

        // Wait for the page to load first
        document.addEventListener('DOMContentLoaded', function () {

            //Get a reference to the links on the page
            var details = document.querySelectorAll('.details');
            var dl = details.length;
            for (i = 0; i < dl; i++) {
                details[i].addEventListener('click', function (event) {
                    modalBody.innerHTML = '';
                    var spinnerDiv = document.createElement('div');
                    spinnerDiv.classList.add('spinner-border');
                    spinnerDiv.classList.add('text-alert');
                    spinnerDiv.classList.add('d-flex');
                    spinnerDiv.classList.add('mt-2');
                    spinnerDiv.classList.add('ml-auto');
                    spinnerDiv.classList.add('mr-auto');
                    spinnerDiv.setAttribute('role', 'status');
                    var span = document.createElement('span');
                    span.classList.add('sr-only');
                    span.innerText = 'Loading...';
                    spinnerDiv.appendChild(span);
                    modalBody.appendChild(spinnerDiv);
                    getDetailsById(event.target.dataset.id);
                    return false;
                });
            }

        });

    </script>
}

Build, run and test.

Modal Entity No Format

The Food class DataAnotations do not get serialized. Let's add a JSON Food model with a Status property.

Edit Entities > Food.cs, add a FoodJsonModel:
public class FoodJsonModel
{
    [JsonProperty(PropertyName = "Status")]
    public string Status { get; set; }
    [JsonProperty(PropertyName = "Id")]
    public int Id { get; set; }
    [JsonProperty(PropertyName = "Name")]
    public string Name { get; set; }
    [JsonProperty(PropertyName = "Food Type")]
    public string FoodType { get; set; }
    [JsonProperty(PropertyName = "Store Cold")]
    public bool ColdStore { get; set; }
    [JsonProperty(PropertyName = "Date")]
    public string Date { get; set; }
}
Edit Index.cshtml.cs > IndexModel, update the page handler:
public async Task<JsonResult> OnGetDetailsByIdAsync(int id = 0)
{
    if (id == 0)
    {
        return new JsonResult(new { Status = "Failed", Errors = "id = 0" });
    }

    var foodDetails = await _context.Foods
        .Where(f => f.Id == id)
        .Select(f => new FoodJsonModel
        {
            Status = "Success",
            Id = f.Id,
            Name = f.Name,
            FoodType = f.FoodType.ToString(),
            ColdStore = f.ColdStore,
            Date = string.Format("{0:MM/dd/yyyy hh:mm tt}", f.Date)
        })
        .FirstOrDefaultAsync();

    if (foodDetails == null)
    {
        return new JsonResult(new { Status = "Failed", Errors = "Details Not Found" });
    }

    return new JsonResult(foodDetails);
}
Edit Index.cshtml, update the JavaScript:
        function getDetailsById(id) {

            return fetch('/index?handler=DetailsById&id=' + id,
                {
                    method: 'get',
                    headers: {
                        'Content-Type': 'application/json;charset=UTF-8'
                    }
                })
                .then(function (response) {
                    if (response.ok) {
                        return response.text();
                    } else {
                        throw Error('DetailsById Response Not OK');
                    }
                })
                .then(function (text) {
                    try {
                        return JSON.parse(text);
                    } catch (err) {
                        throw Error('DetailsById Method Not Found');
                    }
                })
                .then(function (responseJSON) {
                    var dl, dt, dd;
                    modalBody.innerHTML = '';
                    if (responseJSON.Status === 'Success') {
                        dl = document.createElement('dl');
                        for (prop in responseJSON) {
                            dt = document.createElement('dt');
                            dt.textContent = prop;
                            dl.appendChild(dt);
                            dd = document.createElement('dd');
                            dd.textContent = responseJSON[prop].length === 0 ? 'empty' : responseJSON[prop];
                            dl.appendChild(dd);
                        }
                        var successDiv = document.createElement('div');
                        successDiv.classList.add('alert', 'alert-success');
                        successDiv.appendChild(dl);
                        modalBody.appendChild(successDiv);
                    }
                    else {
                        dl = document.createElement('dl');
                        for (prop in responseJSON) {
                            dt = document.createElement('dt');
                            dt.textContent = prop;
                            dl.appendChild(dt);
                            dd = document.createElement('dd');
                            dd.textContent = responseJSON[prop];
                            dl.appendChild(dd);
                        }
                        var alertDiv = document.createElement('div');
                        alertDiv.classList.add('alert', 'alert-danger');
                        alertDiv.appendChild(dl);
                        modalBody.appendChild(alertDiv);
                    }
                })
                .catch(function (error) {
                    modalBody.innerHTML = '';
                    var alertDiv = document.createElement('div');
                    alertDiv.classList.add('alert', 'alert-danger');
                    alertDiv.textContent = 'Error ' + error;
                    modalBody.appendChild(alertDiv);
                })
        }

Build, run and test.

Modal Entity Formatted
Modal Entity Formatted Mobile
Update 08/20/2019

Updated the links to the previous articles in the series.

Update 08/25/2019

Updated articles, asset and topic links.



Comment Count = 0

Please log in to comment or follow.

Login Register
Follow to get web notifications when new comments are posted to this article.
Logged in users receive web notifications for new articles, topics and assets.
Web Notifications