This is my JS script for pushing the student scores to the page model. When I inspect the student score, it has values:
<script>
if (btnTakeScore) {
btnTakeScore.onclick = function (e) {
e.preventDefault();
var rows = document.querySelectorAll('tbody tr');
let studentScores = [];
for (let i = 0; i < rows.length; i++) {
let studentId = rows[i].getAttribute('studentId');
studentScores.push({
studentId: studentId,
sessionId: cboSession.value,
termId: cboTerms.value,
classId: cboClass.value,
subjectScores: []
})
let tds = rows[i].querySelectorAll('td input[type="number"]');
for (let j = 0; j < tds.length; j++) {
// Simulate missing score instead of a 0;
let ctrl = tds[j];
if (ctrl.value !== '') {
studentScores[i].subjectScores.push({
studentId: studentId,
subjectId: tds[j].getAttribute('subjectId'),
score: ctrl.value,
scoreTypeId: cboScoreType.value
});
}
}
}
// call server here to post the data
//const serverUrl = 'https://localhost:44301/api/SaveStudentScores';
const serverUrl = '@Url.Page("/Teacher/StudentScore")?handler=SaveScore';
let r = new XMLHttpRequest();
r.open('post', serverUrl);
r.setRequestHeader('content-Type', 'application/json');
r.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
r.send(JSON.stringify(studentScores));
r.onload = function () {
if (r.status === 200) {
var res = JSON.parse(this.responseText);
if (res.status === true) {
alert(res.message);
// clear the fields.
var inputs = document.querySelectorAll('table#scoreEntry input[type="number"]');
inputs.forEach(d => d.value = '0'); // reset the scores to Zero
}
}
}
}
}
<script>
This is my PageModel OnPost. When it comes here, scores values becomes null:
public async Task<JsonResult> OnPostSaveScore([FromBody]IEnumerable<StudentScoreContainer> scores)
{
try
{
// Code to push date to the database
}
return new JsonResult(new { status = true, messsage = "Student(s) score saved!" });
}
catch (Exception ex)
{
return new JsonResult(new { status = false, message = ex.Message });
}
}
public struct StudentScoreContainer
{
public int studentId { get; set; }
public int classId { get; set; }
public int sessionId { get; set; }
public int termId { get; set; }
public int scoreTypeId { get; set; }
public IEnumerable<SubjectScoreContainer> subjectScores { get; set; }
}
public struct SubjectScoreContainer
{
public int studentId { get; set; }
public int subjectId { get; set; }
public int score { get; set; }
}
and here is my HTML code
<div class="card-body">
<div class="row">
<div class="col-sm-2">
<div class="form-group">
<label>Class Room<span class="text-danger">*
</span></label>
<select id="classRoom" class="select" form="searchForm" required="required" asp-items="@(new SelectList(Model.Classess, "Class.Id","Class.Name"))">
<option value="">Select ClassRoom</option>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label>Session<span class="text-danger">*</span></label>
<select id="session" class="select" required="required" asp-items="@(new SelectList(Model.Sessions, "Id","Name"))">
<option value="">Select Session</option>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label>Term<span class="text-danger">*</span></label>
<select id="term" class="select" required="required" asp-items="@(new SelectList(Model.Terms, "Id","Name"))">
<option value="">Select Term</option>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label>Score Types<span class="text-danger">*</span></label>
<select id="cboScoreType" class="select" required="required" asp-items="@(new SelectList(Model.ScoreTypes, "Id","Name"))">
<option value="">Select ScoreType</option>
</select>
</div>
</div>
<div class="col-md-3">
<label></label>
<p></p>
<button id="btnSearch" type="submit" class="btn btn-success btn-block"> Search </button>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="table-responsive">
<table class="table table-striped custom-table table-nowrap mb-0" id="studentList">
<thead>
<tr>
<th>Student</th>
@foreach (var s in Model.SubjectsByTeacher)
{
<th style="text-align:center">@s.Subject.Name</th>
}
</tr>
</thead>
<tbody>
@foreach (var student in Model.Students)
{
<tr studentId="@student.StudentID">
<td>@(student.FirstName.Trim() + " " + student.LastName.Trim())</td>
@foreach (var subject in Model.SubjectsByTeacher)
{
<td style="text-align:center"><input type="number" min="0" max="100" class="form-control" studentId="@student.StudentID" subjectId="@subject.Subject.Id" /></td>
}
</tr>
}
</tbody>
</table>
<br />
</div>
</div>
<div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<button id="btnTakeScore" class="btn btn-primary">
<i class="fa fa-pen"></i> <b>Take Scores</b>
</button>
</div>
</div>
</div>
</div>
According to your js and razor page codes, I found you directly read the getAttribute value and post it to the page method.
Since the studentId and other property are all int type, but the value you get from the html code is the string. This is the reason why the page model couldn't bind the value well.
I suggest you could convert the rows[i].getAttribute('studentId')
to int in js.
Like below:
Since I don't have the whole js codes and pagemodel's class, I just create a test js model to test it.
You could try to follow below codes to replace the js codes you have.
studentScores:
studentScores.push({
studentId: +studentId,
sessionId: +cboSession.value,
termId: +cboTerms.value,
classId: +cboClass.value,
subjectScores: []
})
subjectScores:
studentScores[i].subjectScores.push({
studentId: +studentId,
subjectId: +tds[j].getAttribute('subjectId'),
score: +ctrl.value,
scoreTypeId: +cboScoreType.value
});
As Joseph Ebenezer commented, we could also use NewtonsoftJson
instead of System.Text.Json
to cover this issue:
Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson
package from the below URL: https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson
Then add this code into startup.cs configure service method:
services.AddRazorPages().AddNewtonsoftJson();