skip to Main Content

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


  1. 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. Add type="button" like below to avoid multiple requesting:

    <button class="btn btn-primary" type="button" onclick="addCurrentNotes()">Submit Added Notes</button>
    

    Change your js like below:

    Notes: Please give the noteDate datetime format default value instead of empty string.

    function addCurrentNotes(){
        var notes=[];
        for(let i =0; i < 2;i++)
        {
            var note = {};
            note.noteTitle="";
            note.noteDate=new Date('0001-01-01');   //change here....
            note.noteContent="Content"+i;
            notes.push(note);
        }
        
        notes=JSON.stringify(notes);       //change here.....
        $.ajax({
            contentType:"application/json; charset=utf-8",
            dataType:'json',
            type:'post',
            url:window.location.href+"?handler=AddNotes",
            data:notes,
            success:function()
            {
                console.log("suceess");
            },
            beforeSend:function(xhr){
                xhr.setRequestHeader("XSRF-TOKEN",
                    $('input:hidden[name="__RequestVerificationToken"]').val());
            }
        })
    }
    

    Add [FromBody] in your backend code:

    public ActionResult OnPostAddNotes([FromBody]List<Note> notes)
    {
        return RedirectToPage();
    }
    

    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.

    Login or Signup to reply.
  2. Sample model

     public class Test
     {
          public int Id { get; set; }
          public string Name { get; set; }
     }
    

    Backend Code

    public IActionResult OnPostSaveData(List<Test> objTest)
    {
        //do whatever you want
    }
    

    And in your frontend file add ajax call as follows

    var urlVal = "@Url.Page("/index/")?handler=SaveData";
    
            $.ajax({
                type: "POST",
                url: urlVal,
                headers:
                {
                    "RequestVerificationToken": $('input:hidden[name="__RequestVerificationToken"]').val()
                },
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("XSRF-TOKEN",
                        $('input:hidden[name="__RequestVerificationToken"]').val());
                },
                data: {
                    "objTest": [{ "ID": 1, "Name": "buddhika" }, { "ID": 2, "Name": "buddhika123" }]
                    },
                success: function (response) {
                },
                failure: function (response) {
                    alert(response.responseText);
                },
                error: function (response) {
                     alert(response.responseText);
                }
            });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search