Submit form and do controller action from ViewComponent in ASP.NET Core
Asked Answered
H

2

21

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.

Humidor answered 14/2, 2016 at 23:26 Comment(2)
How are you calling this ViewComponent?Illuviation
@{ ViewData["Title"] = "Home Page"; } @Component.Invoke("AddListItem", @ViewBag.DefaultListId)Humidor
I
17

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

Illuviation answered 15/2, 2016 at 7:21 Comment(6)
I think the link changed or that section is no longer available... learn.microsoft.com/en-us/aspnet/core/mvc/views/view-componentsVeneer
You mean I can not call HttpPost method in viewcomponent?Antimalarial
@AProgrammer, that's correct. ViewComponents are designed to render stuff and not to process requests.Illuviation
but how do you then handle a ViewComponent where it has functionality which is specific to the ViewComponent.... like a control which has search... where do you put the search action....Pupiparous
Is there some alternative where I can use HttpPost request on partial component? I need have selectlist - dropdownlist on viewcomponent or partialview.Aery
This is honestly a fairly big limitation on VC's. RenderAction() allows us to do get/post with forms which gives us options. I keep hearing VC's is RenderAction() replacement but honestly it doesn't look like it. It seems more limiting sadly. This has been a handful of years, has this been "fixed" yet?Leialeibman
H
9

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("/"); 
    }
Humidor answered 16/2, 2016 at 18:48 Comment(1)
The link is 404Lutero

© 2022 - 2024 — McMap. All rights reserved.