I am building a monthly budget app using ASP.NET MVC, and I want to allow users to create multiple income entries at the same time (maximum of 5 forms on larger screens and 3 on smaller screens).
Currently, I'm working with a ViewModel that holds the list of income forms, but I'm struggling to dynamically add or remove forms in the view. Ideally, users should be able to click a "+" button to add a new income form or an "x" button to remove one.
// GET: Incomes/Create
public IActionResult Create()
{
var model = new MultipleIncomeVM();
ViewData["BudgetId"] = new SelectList(_context.Budgets, "BudgetId", "Name");
ViewData["CategoryId"] = new SelectList(_context.Categories, "CategoryId", "Name");
return View(model);
}
// POST: Incomes/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(MultipleIncomeVM incomevm)
{
try
{
// Validate only the incomes with Amount greater than 0
var validIncomes = incomevm.Incomes.Where(i => i.Amount > 0);
if (ModelState.IsValid)
{
foreach (var i in validIncomes)
{
// Add each income
_context.Add(i);
await _context.SaveChangesAsync();
// A new transaction
var transaction = new Transaction
{
Date = i.DateReceived,
Type = ControllersName.GetControllerName(this.GetType().Name),
Detail = i.Source,
Amount = i.Amount,
BudgetId = i.BudgetId,
CategoryId = i.CategoryId
};
_context.Add(transaction);
await _context.SaveChangesAsync();
}
return RedirectToAction(nameof(Index));
}
}
catch (DbUpdateException)
{
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
ViewData["BudgetId"] = new SelectList(_context.Budgets, "BudgetId", "Name");
ViewData["CategoryId"] = new SelectList(_context.Categories, "CategoryId", "Name");
return View(incomevm);
}
namespace My_Budget.ViewModels
{
public class MultipleIncomeVM
{
public List<Income> Incomes { get; set; } = new List<Income>
{
new Income { DateReceived = DateTime.Today }
};
}
}
@model My_Budget.ViewModels.MultipleIncomeVM
@{
ViewData["Title"] = "Create Incomes";
}
<h1>Create Multiple Incomes</h1>
<hr />
<div class="row">
<div class="col-md-12">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<table class="table table-bordered">
<thead>
<tr>
<th>Source</th>
<th>Amount</th>
<th>Date Received</th>
<th>Category</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.Incomes.Count; i++)
{
<tr>
<td>
<input asp-for="@Model.Incomes[i].Source" class="form-control" />
<span asp-validation-for="@Model.Incomes[i].Source" class="text-danger"></span>
</td>
<td>
<input asp-for="@Model.Incomes[i].Amount" class="form-control" />
<span asp-validation-for="@Model.Incomes[i].Amount" class="text-danger"></span>
</td>
<td>
<input asp-for="@Model.Incomes[i].DateReceived" class="form-control" type="date" />
<span asp-validation-for="@Model.Incomes[i].DateReceived" class="text-danger"></span>
</td>
<td>
<select asp-for="@Model.Incomes[i].CategoryId" class="form-control" asp-items="ViewBag.CategoryId"></select>
<span asp-validation-for="@Model.Incomes[i].CategoryId" class="text-danger"></span>
</td>
<td>
<i class="bi bi-plus-circle" id="add-form"></i>
<i class="bi bi-x-circle" disabled></i>
</td>
<td>
@* Hidden value *@
<select asp-for="@Model.Incomes[i].BudgetId" hidden class="form-control" asp-items="ViewBag.BudgetId" ></select>
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
What I’ve tried so far:
I've created a list in the ViewModel to hold multiple income entries.
I can display multiple forms initially, but I'm unsure how to handle dynamic addition/removal of forms in the view.
My goal is to manage the creation and deletion of income forms in the view(maybe using JS?), so users can interactively add or remove forms as they want.
Thank you!
To enable dynamic addition and removal of income forms in your ASP.NET MVC application, you can use JavaScript (or jQuery) to manipulate the DOM.
@model My_Budget.ViewModels.MultipleIncomeVM
@{
ViewData["Title"] = "Create Incomes";
}
<h1>Create Multiple Incomes</h1>
<hr />
<div class="row">
<div class="col-md-12">
<form asp-action="Create" id="incomeForm">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<table class="table table-bordered" id="incomeTable">
<thead>
<tr>
<th>Source</th>
<th>Amount</th>
<th>Date Received</th>
<th>Category</th>
<th></th>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.Incomes.Count; i++)
{
<tr>
<td>
<input asp-for="@Model.Incomes[i].Source" class="form-control" />
<span asp-validation-for="@Model.Incomes[i].Source" class="text-danger"></span>
</td>
<td>
<input asp-for="@Model.Incomes[i].Amount" class="form-control" />
<span asp-validation-for="@Model.Incomes[i].Amount" class="text-danger"></span>
</td>
<td>
<input asp-for="@Model.Incomes[i].DateReceived" class="form-control" type="date" />
<span asp-validation-for="@Model.Incomes[i].DateReceived" class="text-danger"></span>
</td>
<td>
<select asp-for="@Model.Incomes[i].CategoryId" class="form-control" asp-items="ViewBag.CategoryId"></select>
<span asp-validation-for="@Model.Incomes[i].CategoryId" class="text-danger"></span>
</td>
<td>
<button type="button" class="btn btn-danger remove-form">Remove</button>
</td>
</tr>
}
</tbody>
</table>
<button type="button" class="btn btn-success" id="add-form">Add Income</button>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
<script>
$(document).ready(function () {
var maxForms = 5; // Maximum forms allowed
var formCount = $('#incomeTable tbody tr').length;
// Add form button click event
$('#add-form').click(function () {
if (formCount < maxForms) {
var newRow = `<tr>
<td>
<input name="Incomes[${formCount}].Source" class="form-control" />
</td>
<td>
<input name="Incomes[${formCount}].Amount" class="form-control" />
</td>
<td>
<input name="Incomes[${formCount}].DateReceived" class="form-control" type="date" />
</td>
<td>
<select name="Incomes[${formCount}].CategoryId" class="form-control" asp-items="ViewBag.CategoryId"></select>
</td>
<td>
<button type="button" class="btn btn-danger remove-form">Remove</button>
</td>
</tr>`;
$('#incomeTable tbody').append(newRow);
formCount++;
} else {
alert('Maximum of 5 income entries allowed.');
}
});
// Remove form button click event
$(document).on('click', '.remove-form', function () {
$(this).closest('tr').remove();
formCount--;
});
});
</script>
}