I want to add ListItems from a form in a ViewComponent in an ASP.NET 5, Mvc core application.
The component view (Views\Shared\Components\AddListItem\Default.cshtml):
@model ShoppingList.Models.ListItem
<form asp-action="Create">
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
<!-- some fields omitted for brevity -->
<div class="form-group">
<label asp-for="Description" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
The ViewComponent controller (ViewComponents\AddListItem.cs):
namespace ShoppingList.ViewComponents
{
public class AddListItem : ViewComponent
{
private readonly ApplicationDbContext _context;
public AddListItem(ApplicationDbContext context)
{
_context = context;
}
public IViewComponentResult Invoke(string listId)
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IViewComponentResult> Create (ListItem listItem)
{
_context.ListItem.Add(listItem);
await _context.SaveChangesAsync();
return View(listItem);
}
}
}
The Component is invoked in home.cshtml:
@{
ViewData["Title"] = "Home Page";
}
@Component.Invoke("AddListItem", @ViewBag.DefaultListId)
However, I cannot get this to work. Nothing is added.
Rename AddListItem
to AddListItemViewComponent
. This is a convention that is used by ASP.NET to find the component - that is ViewComponents need to end with a ViewComponent
suffix. If you don't want to do it this way, you could decorate the class with the [ViewComponent]
attribute and set the name property to whatever name you require.
Also, the Create
method will never be invoked and ViewComponents will never respond to an HttPost
because it's only purpose is to display view and not to execute code in response to a postback. The View will only either call Invoke
or its asynchronous version InvokeAsync
and nothing else.
It's a bit confusing at first but an easy way to think about it is like a powerful Partial
in the sense that you can do more stuff in the component class, DI friendly, and is easier to test as well.
The ASP.NET team has a page that explains ViewComponents here
Thanks for your help, @Dealdiane.
In case anyone intested, here is the working code:
Views\Shared\Components\AddListItem\Default.cshtml
<form asp-controller="ListItems" asp-action="QuickCreate">
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="No" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="No" class="form-control" />
<span asp-validation-for="No" class="text-danger" />
</div>
</div>
...
Controllers\ListItemsController.cs
// POST: ListItems/QuickCreate
// Create item without showing view, return to home
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> QuickCreate(ListItem listItem)
{
_context.ListItem.Add(listItem);
await _context.SaveChangesAsync();
return Redirect("/");
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With