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.

Login Register
Logged in users receive web notifications.
Web Notifications