ASP.NET Core 2.2 - Details Modal With JSON Entity
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.
This article continues a series about table functions:
The FREE ASP.NET Core 6.0 - Demos And Utilities Project includes a Table Functions Demo. Access to the source code is free for registered users 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.
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.
Update 12/30/2021
Announced the ASP.NET Core 6.0 - Demos And Utilities Project. Updated articles, asset and topic links.
Comments(0)