I am new to ASP.NET core MVC web app. I have written an app that users can scan in their RFID card id. This task is done in a "Create" view. When they submit the input, the system will capture the card id and scan in time (system time) and save to a data table. I would like the data to be displayed as a partial view in the same view page under the input form after the user submit the inputs. The partial view displays not only one single row that the user just input, but updates the latest 10 inputs in a list.
Remarks: I'm using Visual studio 2022, app created using .NET 8.0, Dapper.
There have no problem for the users to submit input using the input form. I have problem to render the partial view list in the Create view under the input form.
After I added below code in the Create view to render the partial view, error info displayed.
<div id="itemlist">
<partial name="_RecordParialView" model="@Model"></partial>
</div>
Error Info:
An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object.
I believe the problem is that I use @model ScanInRecords.Models.Record
in the Create view, while to display the partial view as a list I need to use @model IEnumerable<ScanInRecords.Models.Record>
. However, if I use IEnumerable in the Create view, there will be error as below:
CS1061: 'IEnumerable<Record>' does not contain a definition for 'CardId' and no accessible extension method 'CardId' accepting a first argument of type 'IEnumerable<Record>' could be found (are you missing a using directive or an assembly reference?)
I tried to use IEnumerable to render the partial view list in the Index view, the list is successfully rendered.
I'm not familiar with jquery ajax language, probably I missed out something that the script can't trigger to render the partial view after clicking the "Submit" button.
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$(function () {
$("#addBtn").click(function (event) {
event.preventDefault();
event.stopImmediatePropagation();
$("#postForm").submit();
$.ajax({
url: "Record/Index",
dataType: "html",
type: "get"
success: function (data) {
$("#itemlist").html(data);
}
});
});
});
</script>
}
The errors come from the Create view and _RecordPartialView files.
Create view codes:
Views > Record > Create
@model ScanInRecords.Models.Record
<h4>Record</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" id="postForm">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="CardId" class="control-label"></label>
<input asp-for="CardId" class="form-control" />
<span asp-validation-for="CardId" class="text-danger"></span>
</div>
<div class="form-group">
<input type="button" id="addBtn" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div id="itemlist">
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
$(function () {
$("#addBtn").click(function (event) {
event.preventDefault();
$.ajax({
url: '@Url.Action("Create", "Record")',
type: "POST",
data: $("#postForm").serialize(),
success: function (data) {
$("#itemlist").html(data);
$("#postForm")[0].reset();
},
error: function () {
alert("Failed to save data. Please try again.");
}
});
});
});
</script>
}
Partial view code
Views > Shared > _RecordPartialView
@model IEnumerable<ScanInRecords.Models.Record>
<table class="table" table-bordered table-striped" border="1">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.CardId)
</th>
<th>
@Html.DisplayNameFor(model => model.ScanInTime)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.CardId)
</td>
<td>
@Html.DisplayFor(modelItem => item.ScanInTime)
</td>
</tr>
}
</tbody>
</table>
RecordController
using Microsoft.AspNetCore.Mvc;
using ScanInRecords.Models;
using ScanInRecords.Repository;
namespace ScanInRecords.Controllers
{
public class RecordController : Controller
{
private IRecordRepository _iRecordRepository;
public RecordController(IRecordRepository iRecordRepository)
{
_iRecordRepository = iRecordRepository;
}
public async Task<IActionResult> Index()
{
var result = await _iRecordRepository.GetAllAsync();
return View(result);
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Record record)
{
if (ModelState.IsValid)
{
record.ScanInTime = DateTime.Now;
await _iRecordRepository.Create(record);
var latestRecords = await _iRecordRepository.GetAllAsync();
return PartialView("_RecordPartialView", latestRecords);
}
return View(record);
}
}
}
RecordRepository
using Dapper;
using ScanInRecords.Data;
using ScanInRecords.Models;
using System.Data;
namespace ScanInRecords.Repository
{
public class RecordRepository : IRecordRepository
{
private readonly IDapperContext _context;
public RecordRepository(IDapperContext context)
{
_context = context;
}
public async Task<IEnumerable<Record>> GetAllAsync()
{
var query = "SELECT TOP 10 * FROM SBSAttendance ORDER BY ScanInTime DESC";
using (var connection = _context.CreateConnection())
{
var result = await connection.QueryAsync<Record>(query);
return result.ToList();
}
}
public async Task Create(Record _Record)
{
var query = @"INSERT INTO SBSAttendance(CardId, ScanInTime) VALUES(@CardId,@ScanInTime)";
DynamicParameters parameters = new DynamicParameters();
parameters.Add("CardId", _Record.CardId, DbType.String);
parameters.Add("ScanInTime", _Record.ScanInTime, DbType.DateTime);
using (var connection = _context.CreateConnection())
{
await connection.ExecuteAsync(query, parameters);
}
}
}
}
Below is the expected partial view below the input form after submitting input, but not successful.
Expected partial view record list.
Your help is very much appreciated.
After testing in my local, it's working in my side. Here is my test code.
Create.cshtml
@model _79322748.Models.Record
<h4>Record</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form id="postForm">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="CardId" class="control-label"></label>
<input asp-for="CardId" class="form-control" />
<span asp-validation-for="CardId" class="text-danger"></span>
</div>
<div class="form-group">
<input type="button" id="addBtn" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div id="itemlist">
</div>
@section Scripts {
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function () {
$("#addBtn").click(function (event) {
event.preventDefault();
$.ajax({
url: '@Url.Action("Create", "Record")',
type: 'POST',
data: $("#postForm").serialize(),
success: function (data) {
$("#itemlist").html(data);
$("#postForm")[0].reset();
},
error: function () {
alert("Failed to save data. Please try again.");
}
});
});
});
</script>
}
RecordController.cs
using System.Diagnostics;
using _79322748.Models;
using Microsoft.AspNetCore.Mvc;
namespace _79322748.Controllers
{
public class RecordController : Controller
{
private readonly ILogger<RecordController> _logger;
public RecordController(ILogger<RecordController> logger)
{
_logger = logger;
}
private static readonly List<Record> Records = new();
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Record record)
{
if (ModelState.IsValid)
{
record.ScanInTime = DateTime.Now;
Records.Add(record);
var latestRecords = Records.OrderByDescending(r => r.ScanInTime).Take(10).ToList();
return PartialView("_RecordPartialView", latestRecords);
}
return View(record);
}
[HttpGet]
public IActionResult GetLatestRecords()
{
var latestRecords = Records.OrderByDescending(r => r.ScanInTime).Take(10).ToList();
return PartialView("_RecordPartialView", latestRecords);
}
}
}
Record.cs
namespace _79322748.Models
{
public class Record
{
public string? CardId { get; set; }
public DateTime ScanInTime { get; set; }
}
}
_RecordPartialView.cshtml
@model IEnumerable<_79322748.Models.Record>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Card ID</th>
<th>Scan In Time</th>
</tr>
</thead>
<tbody>
@foreach (var record in Model)
{
<tr>
<td>@record.CardId</td>
<td>@record.ScanInTime</td>
</tr>
}
</tbody>
</table>