Leveraging HTMX with ASP.NET Core Partials for Dynamic UI Interactions
In the dynamic realm of web development, crafting seamless and interactive user interfaces is paramount for delivering a captivating user experience. ASP.NET Core, a robust and versatile web framework, empowers developers to efficiently construct powerful applications. When integrated with HTMX, a lightweight JavaScript library, developers can elevate user interaction by dynamically updating specific sections of a web page without the need for a full page refresh. One effective strategy to achieve this is through the utilization of partials in ASP.NET Core.
About HTMX
HTMX is a lightweight dependency-free JavaScript library, which empowers developers to create cutting-edge user interfaces with the simplicity and potency of hypertext (markup). By offering direct access to AJAX, CSS Transitions, WebSockets, and Server-Sent Events within HTML using attributes, HTMX transforms the development landscape. This library is a game-changer, enabling developers to achieve interactivity solely through markup.
Partial Views
Partial views in ASP.NET serve as reusable view components embedded within other views. This modular approach aids in breaking down larger views into smaller, more manageable components, reducing code duplication and enhancing application modularity and maintainability.
Partial views, created using the familiar Razor syntax, can display data or execute any action a regular view can. The key distinction lies in the absence of a dedicated layout, with partial views typically employed to render specific portions of a page.
The Challenge
While HTMX facilitates the production of HTML responses from HTTP requests to replace elements in the DOM, crafting such responses within a .NET MVC application can become cumbersome and challenging to maintain, especially with an increasing number of form elements.
Example: "When a user clicks on a <button> to make a POST request to the URL '/api/htmx/form-input/get' and load the response into the inner HTML of the another element like a <div>". These HTML responses are produced and formatted from the server. In .NET MVC app, this can be done in several ways such as string interpolation. One can produce a response like:
string elementInput = $@"<div class='mb-3'>
<label class='form-label' for='{elementName}'>{displayName}</label>
<input type='text' class='form-control {customClass}' id='{elementName}' name='{elementName}' value='{value}' {required}>
</div>";
This approach may negate the advantages of leveraging form tag helpers, which provide benefits such as model binding, validation, automatic HTML attribute generation, and more.
Leveraging HTMX with Partials
To overcome these challenges, we can combine partials and HTMX to achieve a new level of UI interactivity and consistency in our .NET Core application. The following steps assume a basic understanding of working with .NET Core projects.
Step 1. Project Creation
Create a new .NET Core MVC project, naming it "HtmxPartial."
Step 2. The Model
Define a sample model, "FormModel," with attributes representing form inputs.
public class FormModel
{
[Key]
public Guid FormId { get; set; }
[Required]
[Display(Name = "Form Input")]
public string FormInput { get; set; } = null!;
[Required]
[Display(Name = "Select Input")]
public string SelectInput { get; set; } = null!;
[Display(Name = "Partial One")]
public string? PartialOne { get; set; } = string.Empty;
[Display(Name = "Partial Two")]
public string? PartialTwo { get; set; } = string.Empty;
}
Step 3. The HtmxController
Create a controller to handle HTMX requests.
public class HtmxController : Controller
{
[HttpPost("htmx/get-selected-partial")]
public IActionResult GetPartial(FormModel formModel)
{
if (!string.IsNullOrEmpty(formModel.SelectInput))
{
var partialSelected = formModel.SelectInput.ToLower() == "one" ? "_InputOnePartial" : "_InputTwoPartial";
return new PartialViewResult
{
ViewName = $"Partials/{partialSelected}"
};
}
return Ok();
}
}
Step 4. Create Partial Views
Create two partial views, "_InputOnePartial.cshtml" and "_InputTwoPartial.cshtml," within the "Partials" folder.
@model FormModel
<!-- _InputOnePartial.cshtml content -->
<div class="mb-3 mt-3">
<label asp-for="PartialOne" class="form-label"></label>
<input type="text" class="form-control" asp-for="PartialOne" required>
<span asp-validation-for="PartialOne" class="text-danger"></span>
</div>
@model FormModel
<!-- _InputTwoPartial.cshtml content -->
<div class="mb-3 mt-3">
<label asp-for="PartialTwo" class="form-label"></label>
<input type="text" class="form-control" asp-for="PartialTwo" required>
<span asp-validation-for="PartialTwo" class="text-danger"></span>
</div>
Step 5. Modify the Home Controller
Update the HomeController with a post action to handle HTMX requests and return the appropriate partial.
public IActionResult Index(FormModel formModel)
{
if (ModelState.IsValid)
{
//valid post, do more logic here
TempData["Message"] = "Form processed successfully";
return View();
}
//get the partial view to display in form
var partialSelected = formModel.SelectInput.ToLower() == "one" ? "_InputOnePartial" : "_InputTwoPartial";
return View(formModel);
}
Step 6. Modify the Home View
Modify the default Index view for the HomeController to include the form with HTMX functionality.
@model FormModel
@{
ViewData["Title"] = "Sample HTMX Partial";
}
<div class="container-fluid">
<h3 class="text-dark mb-1">
Sample Form
</h3>
<div class="row mt-2">
@if(TempData["Message"] != null)
{
<div class="alert alert-success alert-dismissible">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
@TempData["Message"]
</div>
}
<div class="col-12">
<form asp-action="" asp-controller="home" method="post" id="sampleForm">
<div class="mb-3 mt-3">
<label asp-for="FormInput" class="form-label"></label>
<input type="text" class="form-control" asp-for="FormInput" required>
<span asp-validation-for="FormInput" class="text-danger"></span>
</div>
<div class="mb-3 mt-3">
<label asp-for="SelectInput" class="form-label"></label>
<select class="form-select" asp-for="SelectInput" required
hx-post="/htmx/get-selected-partial"
hx-trigger="load, change delay:200ms"
hx-target="#partial-div"
hx-swap="innerHTML"
hx-indicator="#loading-indicator"
hx-headers="{'Content-Type': 'application/x-www-form-urlencoded'}">
<option value="">Select option</option>
<option value="One">Option One</option>
<option value="Two">Option Two</option>
</select>
<span asp-validation-for="SelectInput" class="text-danger"></span>
</div>
<div class="row mb-3" id="partial-div">
</div>
<div id="loading-indicator" style="display: none;">
Loading...
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
@section scripts{
<script>
// Optional: Initialize jQuery Validation
$(document).ready(function () {
$("#sampleForm").validate({
errorClass: "text-danger",
errorElement: "span"
});
});
</script>
}
Step 7. Add HTMX CDN
Include the HTMX CDN in the layout file.
<script src="https://cdnjs.cloudflare.com/ajax/libs/htmx/1.9.10/htmx.min.js"></script>
<!-- Optionally include jquery-validation if needed... -->
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
Concluding the setup, you can now leverage .NET partials as responses to HTMX requests. The project can be downloaded from my GitHub repository. If you find this project useful, kindly give it a like. Feel free to share this post, leave a comment, or provide feedback—your insights are highly appreciated.
Comments