I have seen a few different questions regards ajax Post methods, but none seem to solve my issue.
I have a Partial View which is creating an array of objects via javascript on the client side, and I want to take this array and then pass it to my page behind files in my Razor Pages application as a C# type of List of T (T being my custom object Note).
Unfortunately, when the data goes back to my OnPostAddNotes method, the List is always empty. The data passes as expected when just using the Note class as the model, but for some reason I cannot get it to pass List of "Note".
The "top level model" is a Character Detail Model, which has a property of CharacterNotes, which is a List of type Note.
Model/Character.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace RPGInfo.Web.Models
{
public class Character : BaseEntity
{
public List<Note> CharacterNotes { get; set; } = new List<Note>();
}
}
Type for CharacterNotes is a List of Note which has these properties.
Model/CharacterNote.cs
using System;
using System.ComponentModel.DataAnnotations;
namespace RPGInfo.Web.Models
{
public class Note : BaseEntity
{
[Required]
[MaxLength(40)]
public string NoteTitle { get; set; }
public DateTime NoteDate { get; set; }
[Required]
[MaxLength(500)]
public string NoteContent { get; set; }
}
}
This is the main View which has as a Model the Character Detail Model. It has a partial of Character Notes.
Views/CharacterDetail.cshtml
@page "{id}"
@model RPGInfo.Web.Pages.CharacterDetailModel
@model {
ViewData["Title"] = "Character Detail";
}
<div>
<partial name="_CharacterNotePartial" model="@Model.CharacterNotes" />
</div>
Partial View for creating the notes. Here is where I am trying to send a List of Note objects to the OnPostAddNote method in the Page behind file.
Shared/_CharacterNotesPartial.cshtml
@using RPGInfo.Web.Models
@model List<Note>
<h4>Character Notes</h4>
<div class="row">
<ul class="list-group col-12" id="newNote">
</ul>
</div>
<form asp-page-handler="AddNotes" class="mt-4 mb-4">
<div class="row">
<div class="form-group col-6">
<label for="NoteTitle">Title</label>
<input type="text" id="noteTitle" name="NoteTitle" class="form-control">
</div>
<div class="form-group col-6">
<label for="NoteDate">Date</label>
<input type="datetime" id="noteDate" name="NoteDate" class="form-control">
</div>
</div>
<div class="form-group">
<label for="NoteContent">Content</label>
<input type="text" id="noteContent" name="NoteContent" class="form-control">
</div>
<button type="button" id="addNoteForm" class="btn btn-primary">Note Add</button>
@Html.AntiForgeryToken()
<button class="btn btn-primary" type="button" onclick="addCurrentNotes(data)"> Submit Added Notes</button>
</form>
<style>
.note-style {
word-wrap: break-word;
}
</style>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script type="text/javascript">
data = [];
$("#addNoteForm").click(function() {
addNote();
});
$("#removeNote").click(function() {
removeNote();
});
function addData(item) {
data.push(item);
console.log(data);
}
function removeData(itemToRemove) {
$(itemToRemove).on("click", itemToRemove, function() {
$(this).remove();
var index = data.indexOf(itemToRemove.text());
console.log(index);
data.splice(index, 1);
console.log(data);
});
}
function addNote() {
var noteTitle = $('input[id="noteTitle"]').val();
var noteDate = $('input[id="noteDate"]').val();
var noteContent = $('input[id="noteContent"]').val();
var listItemId = getListItemId(4).toLowerCase();
console.log(listItemId + ' ' + noteDate + ' ' + noteTitle + ' ' + noteContent);
var listItemToAppend = '<li id="li-'+ listItemId + '" class="list-group-item rmv_btn'+ listItemId + '">'
+ '<div class="row">'
+ '<div class="col-10 note-style">'
+ noteTitle + ' ' + noteDate + ' ' + noteContent
+ '</div>'
+ '<div class="col-2">'
+ '<button type="button" id="btn-'+ listItemId +'" class="rmv_btn'+ listItemId + ' btn btn-danger btn-sm">' + "Remove" + '</button>'
+ '</div>'
+ '</div>'
+ '</li>'
$("#newNote").append(listItemToAppend);
var newItem = $('#newNote').find('#li-'+ listItemId).text();
addData(newItem);
var itemToRemove = $('#newNote').find('#li-'+ listItemId);
removeData(itemToRemove);
}
function getListItemId(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = 4;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() *
charactersLength));
}
return result;
}
function addCurrentNotes() {
var notes = [];
for(let i = 0; i < 2; i++) {
var note = {};
note.noteTitle = "";
note.noteDate = "";
note.noteContent = "Content" + i;
notes.push(note);
}
notes = JSON.stringify(notes);
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'post',
url: window.location.href + "?handler=AddNotes",
data: notes,
success: function() {
window.location.href="url";
},
beforeSend: function (xhr) {
xhr.setRequestHeader("XSRF-TOKEN",
$('input:hidden[name="__RequestVerificationToken"]').val());
}
});
};
</script>
Function and Ajax method for sending data on click=AddCurrentNotes(data). I have tried adding and removing various properties from Note to build the json, but none are being sent to the parameter on the post method.
The page behind file has a "OnPostAddNotes" method which is connected to the submit button and receives a List of Note parameter. The List of Note Parameter always comes back with Count of 0. If I switch from List of Note to just Note as the model, it works as expected, but the List does not.
Pages/Characters/CharacterDetail.cshtml.cs
namespace RPGInfo.Web.Pages
{
public class CharacterDetailModel : PageModel
{
[BindProperty]
public Character Character { get; set; }
public void OnGet(int id)
{
Character = _context.Characters.Where(x => x.Id == id).FirstOrDefault();
Character.CharacterNotes = _context.Notes.Where(note => note.CharacterId == id).ToList();
}
[BindProperty]
public Note CharacterNote { get; set; }
[BindProperty]
public List<Note> CharacterNotes { get; set; }
public ActionResult OnPostAddNotes([FromBody]List<Note> notes)
{
// **List<Note> is null here**
return RedirectToPage();
}
}
On the client side, the notes are created via Javascript, and when looking at the payload, the object looks to be structured correctly, however, the network tab just shows the ajax methods as pending.
Thank you very much for your assistance!
Updated code and image showing the new payload. Changed the date in my ajax function and that is showing in the payload, unfortunately the List is still coming back as null in my CharacterDetail.cshtml.cs file.
2
Answers
Not sure what is your
data
from in your partial view, but it is not important. I see your Payload contains data successfully. So I just hard-coded the Payload for easy testing and give a working demo to you:You can see your
Network
contains two request for this handler, that is because<button>
element default type is submit, so it will submit the form data to backend firstly. Addtype="button"
like below to avoid multiple requesting:Change your js like below:
Notes: Please give the noteDate datetime format default value instead of empty string.
Add
[FromBody]
in your backend code:Notes:
Ajax cannot work with RedirectToPage, if you want to redirect when ajax post back, you need add
window.location.href="url"
in success function.Sample model
Backend Code
And in your frontend file add ajax call as follows