I want to create a nested form just like on ruby on rails form_for for my view using .net mvc.
I have a models that has one to many relationship.
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public int Name { get; set; }
public ICollection<Post> Posts { get; set;}
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int Title { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
and I have this view model:
public class CreateBlogVM
{
public int Name { get; set; }
public ICollection<Post> Posts { get; set;}
}
and a GET Request action that initialize the Posts
[HttpGet]
public ActionResult<CreateBlogVM> Create()
{
var vm = new CreateBlogVM()
{
Posts = [new Posts { Title = "" }]
};
return View(vm);
}
On my view I wanted to create a nested form for the posts properties just like in ruby on rails
@model CreateBlogVM
<form asp method="post" asp-action="Create">
<div class="form-group col-4">
<label asp-for="Name" class="control-label">Name:</label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
@foreach (var post in @Model.Posts)
{
<div class="form-group col-4">
<label asp-for="@post.Title" class="control-label">Title:</label>
<input asp-for="@post.Title" class="form-control" />
<span asp-validation-for="@post.Title" class="text-danger"></span>
</div>
}
</form>
Validating the POST
request I see no posts. Am I missing something? I was trying to look for a documentation about nested forms or nested view models.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(CreateBlogVM model)
{
System.Console.WriteLine($"{string.Join("-", model.Posts)}"); <--- throws an error because Posts here is null
if (ModelState.IsValid)
{
await _blogService.Create(model);
return RedirectToAction(nameof(Index));
}
return View(model);
}
UPDATE:
OK upon investigating. I just found out that the request is null because data its being sent like this:
Title: ""
and html is being formatted like this.
<input name="Title" class="form-control" />
HTML should be like this. The name
property should be like in array format
<input name="Posts[0].Title" class="form-control" />
So I think I have to do it manually since .net mvc doesnt support something like this.
The way I’m thinking to do it and the only way is thru JQuery.
2
Answers
This is how I fixed my problem.
on my HTML I have this:
and on you script section you can do this:
The issue with this solution is. When the model state gets validated then the old data is not being preserved. So the users have to retype there post again.
Update:
ok for the data to be preserved you have to add the value property on your input tag.
Maybe you miss
Include
statement in model loading fromDbContext
?For example:
Or, if you really constructing an object in controller without
DbContext
try to useDto
asView Model
withList<Post>
property.For example:
And configure
AutoMapper
, docs are here.