Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create shared form in ASP.NET CORE Razor Pages?

I have to create a reusable form in my page header. It should be displayed on each page. Simple input and submit button that will send a POST request.

Options that I'm aware of are partial views or view components. I checked a documentation about view components but there is no mention that it works with forms. Only InvokeAsync method is available to initialize a view.

With partial view, it may be hard to define its page model and I don't understand where to put its POST handler.

One other option I see, somehow to place a form directly on _Layout.cshtml, but, again, it doesn't have its page model (afaik),

So what is a way to create a shared form, and where POST request should be handled?

like image 432
Roma Kostelnyy Avatar asked Jan 27 '23 05:01

Roma Kostelnyy


1 Answers

You should use a view component, as this allows you to have a somewhat self-contained model and view interaction.

public class SharedFormViewComponent : ViewComponent
{
    public Task<IViewComponentResult> InvokeAsync() =>
        Task.FromResult(View(new SharedFormViewModel()));
}

Then, put your form HTML code in Views\Shared\Components\SharedForm\Default.cshtml. For your form's action, you'll need to specify a route. More on that in a sec. Then, to display your form:

@await Component.InvokeAsync("SharedForm")

Now, as you've somewhat discerned, view components can't be posted to. It's not an issue of "not supporting forms"; they're literally not part of the request pipeline, and therefore can't respond to requests such as a POST. You'll need a distinct action that will handle the POST on some controller. On your component's form tag, then:

<form asp-action="SharedFormHandlerAction" asp-controller="Foo" asp-area="" method="post">

The asp-area attribute should be provided just in case this is used in the context of different areas.

You'll also need a "return URL". This will be the URL of the current page, so that after the user successfully posts the form, they'll go back to the page they submitted it from. You can achieve that by adding a hidden input to your form:

<input type="hidden" name="returnUrl" value="@(Context.Request.Query["returnUrl"].FirstOrDefault() ?? (Context.Request.Path + Context.Request.QueryString))" />

Your handler action should then take a param like string returnUrl = null, and on success you should do:

return !string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl)
    ? Redirect(returnUrl)
    : RedirectToAction("SomeDefaultAction");

Where things get a little tricky is in handling validation errors. Since you're posting to a different action, you can't return the previous view the user was on to show the validation errors within the from in the layout or whatever. Instead, you'll want a view specific to this handler action which will simply be your shared form. You can use the view for your view component here as a partial:

<partial name="~\Views\Shared\Components\SharedForm\Default.cshtml" />
like image 68
Chris Pratt Avatar answered Feb 05 '23 19:02

Chris Pratt