.NET Core 7 Razor pages: Updating Section of a Razor Page (Partial Update), Without Re-rendering the Whole Content, with Means of Ajax POST





0/5 (0 vote)
On the parent razor page, we add Partial View which will serve as the updatable container. On Ajax calls from the parent page, we update only Partial view content, not the whole page, thus creating a smooth user interaction experience.
Introduction
I've spent quiet some time researching on this topic and couldn't find any place with complete implementation. Even working under the ChatGPT assistance, it took me couple of days to have a final clean solution.
Background
Once again, this is for Razor pages webproject in Visual Studio 2022, but it could be applied to MVC as well as MVC has the same concept of Partial views.
How To
We are going to deal with just three parts of the Razor pages web project: Index.cshtml, Index.cshtml.cs and _SelectTable.cshtml partial view. This is a bare minimum UI without any fancy stuff, to show just the concept. Please refer to this folders structure to have an idea of files location (files of our interest are highlighted in yellow).
The Partial View (_SelectTable.cshtml)
@model TestPartialViewWithAjax.Pages.IndexModel
@{
}
<table id="selectTable">
<thead>
<tr>
<th>Select Element</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@if (Model.SelectedValues != null)
{
@for (var i = 0; i < Model.SelectedValues.Count; i++)
{
<tr>
<td>
<select asp-for="SelectedValues[i]">
@foreach (var item in Model.YourSelectList)
{
var selected = Model.SelectedValues[i] == item.Value ?
"selected" : "";
if (Model.SelectedValues[i] == item.Value)
{
<option value="@item.Value" selected>
@item.Text</option>
} else
{
<option value="@item.Value">@item.Text</option>
}
}
</select>
</td>
<td>
<button type="button" class="deleteRowButton">Delete</button>
</td>
</tr>
}
}
</tbody>
</table>
In this page, we render table rows with select elements. Selected option of existing rows is preserved when a new row is added. Rows can also be deleted, deletion is done by means of pure JavaScript block in the parent cshtml page.
The Parent page (Index.cshtml)
@page
@using Newtonsoft.Json;
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">
building Web apps with ASP.NET Core</a>.</p>
<br /><br />
<h3>List of Nominees</h3>
<form method="post">
<div id="selectTableContainer">
<partial name="_SelectTable" model="Model" />
</div>
<button type="button" id="addRowButton">Add Row</button>
<button type="submit">Submit</button>
</form>
</div>
@section Scripts {
<script>
document.getElementById("addRowButton").addEventListener("click", function () {
// Make an AJAX request to add a new row
// Capture the user's current selections
var selectedValues = [];
$('select').each(function () {
selectedValues.push($(this).val());
});
// Create a JavaScript object that combines both data
var requestData = {
YourSelectList: @Html.Raw
(JsonConvert.SerializeObject(Model.YourSelectList)),
SelectedValues: selectedValues
};
// Serialize the request data
var serializedModel = JSON.stringify(requestData);
$.ajax({
url: '/?handler=AddRow',
method: "POST",
contentType: "application/json; charset=utf-8",
headers: {
RequestVerificationToken:
$('input:hidden[name="__RequestVerificationToken"]').val()
},
data: JSON.stringify(serializedModel),
success: function (result) {
$("#selectTableContainer").html(result);
},
error: function () {
alert("Failed to add a new row.");
}
});
});
// Handle row deletion
$("#selectTableContainer").on("click", ".deleteRowButton", function () {
var row = $(this).closest("tr");
row.remove();
});
</script>
}
We have <partial name="_SelectTable" model="Model" />
partial view included in the <form>
where we add new table rows. Partial view shares parent page's Model. DeleteRow
method uses pure JavaScript and simply manipulates HTML DOM to remove fields from the form. AddRowButton
click uses Ajax call to the page behind AddRow
method. Note, that this post call doesn't re-render the page, it updates partial view portion only.
Submit button is of type "submit" and as such, it posts back and refreshes/redirects the whole page. It submits the array of selections of the whole table, so that the parent knows all user interactions from Partial view.
Here, you could add jquery datatable plugin to have a fancy table stuff like sorting, searching, numbering, drag-and-drop, etc.
The Parent Page Behind (Index.cshtml.cs)
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Newtonsoft.Json;
namespace TestPartialViewWithAjax.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
[BindProperty]
public List<string> SelectedValues { get; set; }= new List<string>();
public List<SelectListItem> YourSelectList { get; set; }
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
// Initialize the YourSelectList with your options
YourSelectList = new List<SelectListItem>
{
new SelectListItem { Value = "Default", Text = "Default",
Disabled = false,Group = null,Selected = false },
new SelectListItem { Value = "Option1", Text = "Option 1",
Disabled = false,Group = null,Selected = false },
new SelectListItem { Value = "Option2", Text = "Option 2",
Disabled = false,Group = null,Selected = false },
new SelectListItem { Value = "Option3", Text = "Option 3",
Disabled = false,Group = null,Selected = false }
};
SelectedValues.Add("Default");
}
public IActionResult OnPost()
{
// breakpoint here to check SelectedValues binding
return RedirectToPage("./Index");
}
public IActionResult OnPostAddRow([FromBody] string serializedModel)
{
// Deserialize the model
var model = JsonConvert.DeserializeObject<IndexModel>(serializedModel);
// Add a new item to the SelectedValues list
model.SelectedValues ??= new List<string>();
model.SelectedValues.Add("Default");
// Return the partial view with the updated model
return Partial("_SelectTable", model);
}
}
}
Here, OnPostAddRow
method simply adds one new item to the list and returns Partial view updated HTML code back to DOM adding more fields to the main form.
Please note, to avoid confusion for some developers, when the OnPostAddRow
method is called from Ajax the IndexModel
class is initialized as new one, with all default/empty properties. There is no any binding happens here ([BindProperty]
is ignored), that's why we provide serialized data from the parent to send it to the Partial view.
Conclusion
The code is pasted from the workable VS 2022 solution and should be ready for running/testing/reviewing/debugging. The only thing that might be needed for successful compiling is to add Newtonsoft.Json package (if VS 2022 won't do this automatically for you).
History
- 26th September, 2023: Initial version