Is it possible to generate new instances of the same form by clicking a button? - asp.net-mvc

I'm working on a use case in my animal shelter web application where customers are able to register one or more animals at the same time. Ideally I'd like a button on the bottom left that generates another instance of the same form when clicked, so that multiple animal registrations can be saved to the database at once.
NewAnimalRegistration.cshtml:
#model NewAnimalRegistrationViewModel
<html>
<head>
<title>Register an animal</title>
<link rel="stylesheet" href="~/css/style.css"/>
</head>
<body>
<div class="container py-5">
<div class=" row">
<div class="col-md-10" mx-auto>
<div asp-validation-summary="All"></div>
<h1>Animal registration</h1>
<p>
We are happy to hear that you are interested in placing your animal in our shelter. Please fill in the fields below and our system will
check if there is room for your animal.
</p>
<form asp-action="RegistrationForm" method="post">
<div class="form-group row mt-5">
<div class="col-sm-6">
<label asp-for="Name">Name</label>
<input asp-for="Name" class="form-control"/>
</div>
</div>
<div class="form-group row mt-5">
<div class="col-sm-4">
<label asp-for="Gender" class="mr-3">Gender</label>
<select class="form-group" asp-for="Gender" asp-items="#ViewBag.Genders"></select>
</div>
<div class="col-sm-4">
<label asp-for="Type" class="mr-3">Animal type</label>
<select class="form-group" asp-for="Type" asp-items="#ViewBag.AnimalTypes"></select>
</div>
<div class="col-sm-4">
<label>Neutered</label>
<div class="form-check">
<input asp-for="IsNeutered" class="form-check-input" type="radio" value="true">
<label class="form-check-label" asp-for="IsNeutered">
Yes
</label>
</div>
<div class="form-check">
<input asp-for="IsNeutered" class="form-check-input" type="radio" value="false">
<label class="form-check-label" asp-for="IsNeutered">
No
</label>
</div>
</div>
</div>
<div class="form-group mt-5">
<label asp-for="Reason">Why are you deciding to put this animal up for adoption?</label>
<textarea class="form-control" asp-for="Reason" rows="6"></textarea>
</div>
<div class="float-right">
<a asp-controller="Home" asp-action="Index" class="btn btn-primary px-4">Cancel</a>
<button class="btn btn-primary px-4">Save</button>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
Is there a way to do this in .NET Core MVC? If yes, will I simply receive a list of all animal registrations through which I can simply loop and add them all to the database?

I made a demo based on your description, you can refer to it:
Model:
public class NewAnimalRegistrationViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public string Type { get; set; }
public bool IsNeutered { get; set; }
public string Reason { get; set; }
}
Index.cshtml:
#model NewAnimalRegistrationViewModel
<html>
<head>
<title>Register an animal</title>
</head>
<body>
<div class="container py-5">
<div class=" row">
<div class="col-md-10" mx-auto>
<div asp-validation-summary="All"></div>
<h1>Animal registration</h1>
<p>
We are happy to hear that you are interested in placing your animal in our shelter. Please fill in the fields below and our system will
check if there is room for your animal.
</p>
<form asp-action="RegistrationForm" method="post">
<div class="float-right">
<a asp-controller="Home" asp-action="Index" class="btn btn-primary px-4">Cancel</a>
<button class="btn btn-primary px-4">Save</button>
</div>
</form>
<a id="add" href='#' class="text-danger">register another animal</a>
</div>
</div>
</div>
</body>
</html>
#section scripts{
<script>
var count = 0;
$(function () {
var actionUrl = "/Home/AddRegistrationForm?count=" + count;
$.get(actionUrl).done(function (data) {
$('body').find('.float-right').before(data);
});
})
$("#add").on("click", function (e) {
e.preventDefault();
count++;
var actionUrl = "/Home/AddRegistrationForm?count=" + count;
$.get(actionUrl).done(function (data) {
$('body').find('.float-right').before(data);
});
})
</script>
}
_RegisterPartial.cshtml:
#model NewAnimalRegistrationViewModel
#{
int i = ViewBag.Count;
}
<h3>Anaimal #i</h3>
<div class="form-group row mt-5">
<div class="col-sm-6">
<label asp-for="Name">Name</label>
<input asp-for="Name" name="[#i].Name" class="form-control" />
</div>
</div>
<div class="form-group row mt-5">
<div class="col-sm-4">
<label asp-for="Gender" class="mr-3">Gender</label>
<select class="form-group" asp-for="Gender" name="[#i].Gender" asp-items="#ViewBag.Genders"></select>
</div>
<div class="col-sm-4">
<label asp-for="Type" class="mr-3">Animal type</label>
<select class="form-group" asp-for="Type" name="[#i].Type" asp-items="#ViewBag.AnimalTypes"></select>
</div>
<div class="col-sm-4">
<label>Neutered</label>
<div class="form-check">
<input asp-for="IsNeutered" name="[#i].IsNeutered" class="form-check-input" type="radio" value="true">
<label class="form-check-label" asp-for="IsNeutered">
Yes
</label>
</div>
<div class="form-check">
<input asp-for="IsNeutered" name="[#i].IsNeutered" class="form-check-input" type="radio" value="false">
<label class="form-check-label" asp-for="IsNeutered">
No
</label>
</div>
</div>
</div>
<div class="form-group mt-5">
<label asp-for="Reason">Why are you deciding to put this animal up for adoption?</label>
<textarea class="form-control" asp-for="Reason" name="[#i].Reason" rows="6"></textarea>
</div>
Controller:
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult AddRegistrationForm(int count)
{
ViewBag.Count = count;
ViewBag.Genders = new List<SelectListItem>
{
new SelectListItem{ Text = "Female", Value="Female"},
new SelectListItem{ Text = "Male", Value="Male"}
};
ViewBag.AnimalTypes = new List<SelectListItem>
{
new SelectListItem{ Text = "Cat", Value="Cat"},
new SelectListItem{ Text = "Dog", Value="Dog"}
};
return PartialView("_RegisterPartial");
}
[HttpPost]
public IActionResult RegistrationForm(List<NewAnimalRegistrationViewModel> model)
{
return View();
}
Result:

Related

Calling a view from different models in ASP.NET MVC

In my ASP.NET MVC application in the view, I'm calling another view that is not related to the current model. There I need some help that how to call the different model views from another view.
#model Asp_PASMVC.Models.VehicleService
#using Asp_PASMVC.Infrastructure
#{
ViewBag.Title = "View";
Layout = "~/Views/Shared/_Layout.cshtml";
List<SelectListItem> CompanyList = (List<SelectListItem>)TempData.Peek("ComapnyList");
List<SelectListItem> ReqTypes = (List<SelectListItem>)TempData.Peek("RequestTyleList");
List<SelectListItem> Employees = (List<SelectListItem>)TempData.Peek("EmployeeList");
List<SelectListItem> Location = (List<SelectListItem>)TempData.Peek("LocationList");
Asp_PASMVC.Models.AppRequest RequestDetails = (Asp_PASMVC.Models.AppRequest)TempData.Peek("RequestDetails");
}
#{
Html.RenderPartial("_MainRequestView", RequestDetails);
}
#using (Html.BeginForm("WorkshopUpdate", "VehicleService", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.HiddenFor(model => model.Req_Id)
#Html.AntiForgeryToken()
if (Model != null && Model.VehicleServiceApproveDetails != null)
{
foreach (Asp_PASMVC.Models.VehicleServiceApproveDetails Emp in Model.VehicleServiceApproveDetails)
{
Html.RenderPartial("_WorkshopUpdate", Emp);
}
}
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<!-- Default box -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Approver Details</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse" title="Collapse">
<i class="fas fa-minus"></i>
</button>
</div>
</div>
<div class="card-body">
<div>
<fieldset id="pnlApproverList" style="display:none">
<legend><h5>To whom you want to send this request for approval ? </h5> </legend>
<br />
<ul id="RequApprover" style="list-style-type: none">
#if (Model != null && Model.ApprovalPartyList != null)
{
foreach (Asp_PASMVC.Models.ApprovalParty Emp in Model.ApprovalPartyList)
{
Html.RenderPartial("_ApprovalView", Emp);
}
}
</ul>
<button type="button" id="addAnotherApprover" class="btn btn-success" href="#" onclick="this.style.display = 'none';">Add</button>
<script type="text/javascript">
$(function () {
// $("#movieEditor").sortable();
$("#addAnotherApprover").click(function () {
$.get('/VehicleService/AddApproverToReq', function (template) {
$("#RequApprover").append(template);
});
});
});
</script>
<br />
</fieldset>
</div>
</div>
<!-- /.card-footer-->
</div>
<!-- /.card -->
</div>
</div>
</div>
</section>
<div class="card-footer">
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Update and Sent" class="btn btn-success" />
</div>
</div>
</div>
}
<p>
#Html.ActionLink("Back to List", "Index")
</p>
So likewise here the model is VehicleService. So within that view, I want to call another view that is not within the vehicleservice model.
But I cannot load that partial view within this view. Is there any way to do this?
#model Asp_PASMVC.Models.ApprovalParty
#using Asp_PASMVC.Infrastructure
#{
string UserLvel = TempData.Peek("UserLevelClaims").ToString();
}
<li style="padding-bottom:15px">
#using (Html.BeginCollectionItem("ApprovalPartyList"))
{
<div class="row">
<div class="col-md-5 col-sm-5">
<div class="form-group">
<label>
#Html.RadioButtonFor(m => m.Approve_Type, false)
<span class="radiomargin">For Manager</span>
</label>
<br />
#if (UserLvel != "1")
{
<label>
#Html.RadioButtonFor(m => m.Approve_Type, true)
<span class="radiomargin">For Top Manager </span>
</label>
#Html.ValidationMessageFor(model => model.Approve_Type, "", new { #class = "text-danger" })
}
</div>
</div>
</div>
<br />
<div class="row">
<div class="col-md-6 col-sm-6">
<div class="form-group row">
Select the Approver
<div class="col-sm-8">
#Html.DropDownListFor(model => model.Approver_Id, new List<SelectListItem>(), new { #id = "ddlEmployees", #class = "js-dropdown" })
#Html.ValidationMessageFor(model => model.Approver_Id, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
}
</li>
Create a ViewModel which can have both Properties and pass that viewmodel to View
Model class:
public class VehicleSerivceViewModel
{
public VehicleService VehicleService { get; set; }
public ApprovalParty ApprovalParty { get; set; }
}
In View :
#model Asp_PASMVC.Models.VehicleServiceVewModel
pass ViewModel to partial as below:
#Model.ApprovalParty

ASP.NET Core MVC validate not required fields

My page is validating a field that is not required when I submit, even though there is no validation configured for this field.
Create.cshtml
#model Lawtech.App.ViewModels.ProcessoViewModel
#{
ViewData["Title"] = "Novo processo";
}
<h3 style="padding-top: 10px">#ViewData["Title"] </h3>
<hr />
<div class="row">
<div class="col-md-12">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row">
<div class="form-group col-md-4">
<label asp-for="Numero" class="control-label"></label>
<input asp-for="Numero" class="form-control" />
<span asp-validation-for="Numero" class="text-danger"></span>
</div>
<div class="form-group col-sm-4">
<label asp-for="IdArea" class="control-label"></label>
<div class="input-group">
<select id="slcArea" asp-for="IdArea" class="form-control select2"></select>
<div class="input-group-btn">
<a asp-action="CreateArea" class="btn btn-info" style="border-radius:0 0.25rem 0.25rem 0" data-modal="">
<span class="fa fa-plus"></span>
</a>
</div>
</div>
</div>
</div>
<div class="row">
<div class="form-group col-md-6 mt-4">
<input type="submit" value="Cadastrar" class="btn btn-sm btn-primary" />
<a class="btn btn-sm btn-info" asp-action="Index">Voltar</a>
</div>
</div>
</form>
</div>
</div>
<div id="myModal" class="modal fade in">
<div class="modal-dialog">
<div class="modal-content">
<div id="myModalContent"></div>
</div>
</div>
</div>
ViewModel
public class ProcessoViewModel
{
[Key]
public int Id { get; set; }
[DisplayName("Número")]
[Required(ErrorMessage = "O campo número é obrigatório")]
public string Numero { get; set; }
[DisplayName("Área")]
public int IdArea { get; set; }
}
Controller
In Controller Create method, nothing happens, because all validation takes place on the client side.
[Route("novo-processo")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ProcessoViewModel processoViewModel)
{
try
{
if (!ModelState.IsValid) return View(processoViewModel);
await _processoBLL.Insert(_mapper.Map<ProcessoDTO>(processoViewModel));
if (!ValidOperation()) return View(processoViewModel);
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
Inspecting in Chrome I see this generated html for the field that I didn't require validation, I don't know if it could be something related to Jquery.Unobtrusive but I can't remove it either because other fields will be validated.
<select id="slcArea" class="form-control select2 select2-hidden-accessible input-validation-error" data-val="true" data-val-required="The Área field is required." name="IdArea" data-select2-id="slcArea" tabindex="-1" aria-hidden="true" aria-describedby="slcArea-error" aria-invalid="true"></select>
Why is this validation taking place that I have not defined the field as required?
Not nullable properties (that is properties with value types) are always required. Use nullable types (reference types) for properties if they should not be required - eg. int?.
You can always use formnovalidate on any input you don't want validated.
<input asp-for="Numero" formnovalidate="formnovalidate" class="form-control" />
This way there is no need to change your model. This is demonstrated at W3Schools

Get request from RazorPage with ViewComponent

I tray use ViewComponent in Razor Page with condition,
and each Viewcomponents are separate,
My razorpage is "/Subfolder/Index.cshtml"
<div class="row">
<div class="col-md-4">
#await Component.InvokeAsync("RightMenu")
</div>
<div class="col-md-8">
#if (Model.SIndex != 0)
{
#await Component.InvokeAsync("SubContent", new { id = Model.SIndex })
}
#if (Model.SIndex == 15)
{
<div class="row">
<div class="col-md-12">
<form method="post">
#await Component.InvokeAsync("QuestionUs", new { askLibrarian = new Lib.Model.AskLibrarian() })
<div class="form-group">
<input type="submit" value="ask Question" class="btn btn-default" asp-page-handler="question" />
</div>
</form>
</div>
</div>
}
</div>
and code behind of this is "/subfolder/index.cshtml.cs"
public class IndexModel : PageModel
{
private readonly Lib.Model.LibContext _context;
[BindProperty]
public int SIndex { get; set; }
public async Task OnGet(int Id)
{
SIndex = Id;
}
[BindProperty]
public AskLibrarian AskLibrarian { get; set; }
public async Task<IActionResult> OnPostquestionAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.AskLibrarians.Add(AskLibrarian);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Now "questionViewcomponent" is a simple form that show many input elements
in "/subfolder/component/questionus/default.cshtml"
#model Lib.Model.AskLibrarian
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-row">
<div class="form-group col-md-6">
<label asp-for="FullName" class="control-label"></label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-user"></i></div>
</div>
<input asp-for="FullName" class="form-control" />
<span asp-validation-for="FullName" class="text-danger"></span>
</div>
</div>
<div class="form-group col-md-6">
<label asp-for="Email" class="control-label"></label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-envelope"></i></div>
</div>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label asp-for="LibraryNameId" class="control-label"></label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-book"></i></div>
</div>
<select asp-for="LibraryNameId" class="form-control" asp-items="ViewBag.LibraryNameId"></select>
</div>
</div>
<div class="form-group col-md-8">
<label asp-for="Subject" class="control-label"></label>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">
<div class="input-group-text"><i class="fas fa-book-reader"></i></div>
</div>
<input asp-for="Subject" class="form-control" />
<span asp-validation-for="Subject" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label asp-for="Text" class="control-label"></label>
<textarea asp-for="Text" class="form-control" style="min-height:250px;"></textarea>
<span asp-validation-for="Text" class="text-danger"></span>
</div>
set breakpoint on "OnpostQuestionAsync", When I click on submit button with "question" handler do nothing and show me a blank page instead question form.
how can I resolve That
After a long Time my problem resolved by remove
<input type="submit" value="ask Question" class="btn btn-default" asp-page-handler="question" />
and add in form tag
<form method="post" sp-page-handler="question">

How to fill out fields in one form for multiple objects in Thymeleaf?

I have a following models (without getters and setters for readability):
#Entity
public class Recipe extends BaseEntity {
private String name;
private String description;
private Category category;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<Ingredient> ingredients;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<Instruction> instructions;
#ManyToMany
private List<User> administrators;
private int preparationTime;
private int cookTime;
public Recipe(){
super();
ingredients = new ArrayList<>();
instructions = new ArrayList<>();
administrators = new ArrayList<>();
}
public Recipe(String name, String description, Category category, int preparationTime, int cookTime) {
this();
this.name = name;
this.description = description;
this.category = category;
this.preparationTime = preparationTime;
this.cookTime = cookTime;
}
*
#Entity
public class Ingredient extends BaseEntity {
private String name;
private String condition;
private double quantity;
private Measurement measurement;
#ManyToOne
private Recipe recipe;
public Ingredient(){
super();
}
public Ingredient(String name, String condition, double quantity, Measurement measurement) {
this();
this.name = name;
this.condition = condition;
this.quantity = quantity;
this.measurement = measurement;
}
*
#Entity
public class Instruction extends BaseEntity {
private String name;
private String description;
#ManyToOne
private Recipe recipe;
public Instruction(){
super();
}
public Instruction(String name, String description) {
this();
this.name = name;
this.description = description;
}
What i need to do is to fill out fields for each object in one Thymeleaf form and POST it. I know how to do it with a single object. Please explain how to set up the from and controller for multiple objects, so in the end ill have recipe posted with ingredients and instructions list. Thanks!
EDITED:
Here is a controller methods:
#RequestMapping("/recipes/add")
public String formNewRecipe(Model model) {
Recipe recipe = new Recipe();
if (!model.containsAttribute("recipe")) {
model.addAttribute("recipe", recipe);
}
model.addAttribute("action", "/recipes");
model.addAttribute("heading", "New Recipe");
model.addAttribute("submit", "Save");
model.addAttribute("categories", Category.values());
model.addAttribute("measurements", Measurement.values());
return "edit";
}
#RequestMapping(value = "/recipes", method = RequestMethod.POST)
public String addRecipe(#Valid Recipe recipe,
BindingResult result,
RedirectAttributes redirectAttributes) {
if (result.hasErrors()) {
redirectAttributes
.addFlashAttribute("org.springframework.validation.BindingResult.recipe", result);
redirectAttributes.addFlashAttribute("recipe", recipe);
return "redirect:/recipes/add";
}
recipes.save(recipe);
redirectAttributes.addFlashAttribute("flash",
new FlashMessage("New Recipe Created!!!", FlashMessage.Status.SUCCESS));
return "redirect:/recipes/" + recipe.getId();
}
and Thymeleaf form:
<form th:action="#{${action}}" method="post" th:object="${recipe}">
<div class="grid-100 row controls">
<div class="grid-50">
<h2 th:text="${heading}"></h2>
</div>
<div class="grid-50">
<div class="flush-right">
<input class="button" type="submit" th:value="${submit}"/>
<a th:href="#{|/recipes|}" class="secondary">
<button class="secondary">Cancel</button>
</a>
</div>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Name </label>
</p>
</div>
<div class="grid-40">
<p><input type="text" th:field="*{name}"/>
<div class="error-message"
th:if="${#fields.hasErrors('name')}"
th:errors="*{recipe.name}">
</div>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Description </label>
</p>
</div>
<div class="grid-40">
<p><textarea rows="4" th:field="*{description}"></textarea>
<div class="error-message"
th:if="${#fields.hasErrors('description')}"
th:errors="*{recipe.description}">
</div>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Category </label>
</p>
</div>
<div class="grid-30">
<p>
<select th:field="*{category}">
<option value="" disabled="disabled">Recipe Category</option>
<option th:each="c : ${categories}"
th:value="${c.name}"
th:text="${c.name}">All Categories</option>
</select>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Prep Time </label>
</p>
</div>
<div class="grid-20">
<p>
<input type="number" th:field="*{preparationTime}"/>
<div class="error-message"
th:if="${#fields.hasErrors('preparationTime')}"
th:errors="*{preparationTime}"></div>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Cook Time </label>
</p>
</div>
<div class="grid-20">
<p>
<input type="number" th:field="*{cookTime}"/>
<div class="error-message"
th:if="${#fields.hasErrors('cookTime')}"
th:errors="*{cookTime}"></div>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Ingredients </label>
</p>
</div>
<div class="grid-20">
<p class="label-spacing">
<label> Item </label>
</p>
</div>
<div class="grid-20">
<p class="label-spacing">
<label> Condition </label>
</p>
</div>
<div class="grid-15">
<p class="label-spacing">
<label> Quantity </label>
</p>
</div>
<div class="grid-20">
<p class="label-spacing">
<label> Measurement </label>
</p>
</div>
<div class="ingredient-row">
<div class="prefix-20 grid-20">
<p>
<input type="text" th:field="*{ingredients[0].name}"/>
<div class="error-message"
th:if="${#fields.hasErrors('ingredients[0].name')}"
th:errors="*{ingredients[0].name}"></div>
</p>
</div>
<div class="grid-20">
<p>
<input type="text" th:field="*{ingredients[0].condition}"/>
<div class="error-message"
th:if="${#fields.hasErrors('ingredients[0].condition')}"
th:errors="*{ingredients[0].condition}"></div>
</p>
</div>
<div class="grid-15">
<p>
<input type="number" th:field="*{ingredients[0].quantity}"/>
<div class="error-message"
th:if="${#fields.hasErrors('ingredients[0].quantity')}"
th:errors="*{ingredients[0].quantity}"></div>
</p>
</div>
<div class="grid-20">
<p>
<select th:field="*{ingredients[0].measurement}">
<option value="" disabled="disabled">Measurement</option>
<option th:each="i : ${measurements}"
th:value="${i.name}"
th:text="${i.name}">Unknown
</option>
</select>
</p>
</div>
</div>
<div class="prefix-20 grid-80 add-row">
<p>
<button>+ Add Another Ingredient</button>
</p>
</div>
</div>
<div class="clear"></div>
<div class="grid-100 row">
<div class="grid-20">
<p class="label-spacing">
<label> Instructions </label>
</p>
</div>
<div class="grid-20">
<p class="label-spacing">
<label> Steps </label>
</p>
</div>
<div class="grid-60">
<p class="label-spacing">
<label> Description </label>
</p>
</div>
<div class="instruction-row">
<div class="prefix-20 grid-20">
<p>
<input type="text" th:field="*{instructions[0].name}"/>
<div class="error-message"
th:if="${#fields.hasErrors('instructions[0].name')}"
th:errors="*{instructions[0].name}"></div>
</p>
</div>
</div>
<div class="instruction-row">
<div class="grid-50">
<p>
<input type="text" th:field="*{instructions[0].description}"/>
<div class="error-message"
th:if="${#fields.hasErrors('instructions[0].description')}"
th:errors="*{instructions[0].description}"></div>
</p>
</div>
</div>
<div class="prefix-20 grid-80 add-row">
<p>
<button>+ Add Another Step</button>
</p>
</div>
</div>
<div class="clear"></div>
<div class="row"> </div>
</form>
It looks something like this:
<form th:object="${recipe}">
<input type="text" th:field="*{ingredients[0].name}" />
<input type="text" th:field="*{ingredients[0].description}" />
<input type="text" th:field="*{instructions[1].name}" />
<input type="text" th:field="*{instructions[1].description}" />
</form>
If you have a dynamic amount of ingredients, the th:each might look like this:
<th:block th:each="ingredient,i : ${recipe.ingredients}">
<input type="text" th:field="*{ingredients[__${i.index}__].name}" /><br />
<input type="text" th:field="*{ingredients[__${i.index}__].condition}" /><br />
<input type="text" th:field="*{ingredients[__${i.index}__].quantity}" /><br />
<input type="text" th:field="*{ingredients[__${i.index}__].measurement.anotherField}" /><br />
</th:block>
Dynamically adding another ingredient to the form is kind of painful... you either have to submit the form and modify the Recipe object in a controller (adding an ingredient, then redirecting back to the form). Or you can use javascript to copy the fields, making sure the name/id/etc match the others, with the index incremented.

ASP .NET core razor view conditional display doesn't work as expected

I have a view where I conditionally iterate and print items: SPANs are not displayed (as expected).
<div>
#if (Model.SomeCondition)
{
#foreach (var x in Model.SomeData)
{
<span>#x.Title</span>
}
}
</div>
Now I'd like not to display the enclosing DIV, however it doesn't work: SPANs are still not displayed, but the DIV is. Why does this happen?
#if (Model.SomeCondition)
{
<div>
#foreach (var x in Model.SomeData)
{
<span>#x.Title</span>
}
</div>
}
This is the full view code:
#using Team.L
#using Microsoft.AspNetCore.Mvc.Localization
#inject IViewLocalizer Localizer
#model Team.ViewModels.DeptTaskGroupView
#{
var EventHandler = "Aventin." + ViewBag.AspAction + "(event)";
}
<form asp-controller="ModulesEx" asp-action="#ViewBag.AspAction" onsubmit="return false">
<div class="form-horizontal">
<input type="hidden" asp-for="Errors">
<div class="MessageBox">#Html.Raw(Utility.GetMessage(Model))</div>
<div class="form-group col-md-offset-2">
<label class="col-md-2"></label>
<div class="col-md-10">
<span data-valmsg-for="" class="text-danger" />
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">#MyText.Department</label>
<div class="col-md-10">
<select asp-for="DepartmentID" class="form-control" asp-items="#Model.DepartmentList" onchange="#EventHandler"></select>
<span asp-validation-for="DepartmentID" class="text-danger" />
</div>
</div>
#if (Model.AssignedTaskGroup != null)
{
<div class="form-group">
<label class="col-md-2 control-label">#MyText.TaskGroup</label>
<div class="col-md-10">
#foreach (var x in Model.AssignedTaskGroup)
{
<input type="checkbox" name="selectedItems" value="#x.ID" #(Html.Raw(x.Assigned ? "checked=\"checked\"" : "")) />
#x.Title
}
</div>
</div>
}
<div class="form-horizontal">
<div class="form-group">
<div class="col-md-1 col-md-offset-2">
<input type="submit" value="#MyText.Save" class="btn btn-default" />
</div>
</div>
</div>
</div>
</form>
As written, it doesn't happen. I'm guessing you either didn't reload the page or there is code around your demonstrated code that renders a div that this div is contained in.

Resources