dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.

Home Page:https://asp.net

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

EditForm requires either a Model parameter, or an EditContext parameter, please provide one of these.

sam-wheat opened this issue · comments

I am very sorry I am unable to figure this out on my own.
Here are links I have found that have not assisted me in resolving this:

https://learn.microsoft.com/en-us/aspnet/core/blazor/forms/binding?view=aspnetcore-8.0
https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0
https://stackoverflow.com/questions/67641707/editform-requires-either-a-model-parameter-or-an-editcontext-parameter
dotnet/AspNetCore.Docs#14869
#51420 // supplying formname as shown here does not work.
#43400 // applies to Maui however I am newing the model as suggested by the ticket.

<EditForm Model="Input" method="post" OnSubmit="loginClickHandler" FormName="login">
		<div style="height:100%;display:flex;flex-direction:column">
		<DataAnnotationsValidator />
		<h2>Use a local account to log in.</h2>
		<hr />
		<ValidationSummary class="text-danger" role="alert" />
			<MudContainer Class="d-flex flex-column align-self-center align-center gap-4 control-container">
				<MudTextField @bind-Value="Input.UserName" For="@(() => Input.UserName)" @ref=userNameTextField Label="User name" Variant="Variant.Filled" Style="width:250px"></MudTextField>
				<MudTextField @bind-Value="Input.Password" For="@(() => Input.Password)" Label="Password" Variant="Variant.Filled"
					InputType="@PasswordInput" Adornment="Adornment.End" AdornmentIcon="@PasswordInputIcon" 
					OnAdornmentClick="TogglePasswordVisibility" AdornmentAriaLabel="Show Password" Style="width:250px">
				</MudTextField>
				<MudButton ButtonType="ButtonType.Submit"  Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Login">Login</MudButton>
			</MudContainer>
			<div style="display:flex;margin-left:auto;margin-right:auto;margin-top:20vh;">
					<MudImage Style="width:20vw;" Src="/pp.png"></MudImage>
			</div>
		</div>
</EditForm>
@code{

	[SupplyParameterFromForm]
	public LoginRequest Input { get; set; } = new LoginRequest { UserName = string.Empty, Password = string.Empty };
}


public class LoginRequest
{
	public string UserName { get; set; }
	public string Password { get; set; }
}

Server side App.razor:

The route for the form above is "/Account/Login". As shown below when I change the render mode to InteractiveServerRenderMode the page is not found.

@code {
	[CascadingParameter]
	private HttpContext HttpContext { get; set; } = default!;

	private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account")
		? null   // new InteractiveServerRenderMode(prerender: false) results in "Not found"
		: new InteractiveWebAssemblyRenderMode(prerender: false);
}

When I remove [SupplyParameterFromForm] the form submits however the values are empty strings (not bound to user input).

Error message:

Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HN3JPDFNRESR", Request id "0HN3JPDFNRESR:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: EditForm requires either a Model parameter, or an EditContext parameter, please provide one of these.
at Microsoft.AspNetCore.Components.Forms.EditForm.OnParametersSet()
at Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync()
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange1 oldTree, ArrayRange1 newTree)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
at Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(Int32 componentId, RenderFragment renderFragment)
at Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged()
at Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync()
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(Int32 componentId, ParameterView initialParameters)
at Microsoft.AspNetCore.Components.HtmlRendering.Infrastructure.StaticHtmlRenderer.BeginRenderingComponent(IComponent component, ParameterView initialParameters)
at Microsoft.AspNetCore.Components.Endpoints.EndpointHtmlRenderer.RenderEndpointComponent(HttpContext httpContext, Type rootComponentType, ParameterView parameters, Boolean waitForQuiescence)
at Microsoft.AspNetCore.Components.Endpoints.RazorComponentEndpointInvoker.RenderComponentCore(HttpContext context)
at Microsoft.AspNetCore.Components.Endpoints.RazorComponentEndpointInvoker.RenderComponentCore(HttpContext context)
at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<b__10_0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryMiddleware.InvokeAwaited(HttpContext context)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.WebTools.BrowserLink.Net.BrowserLinkMiddleware.InvokeAsync(HttpContext context)
at Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware.InvokeAsync(HttpContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

"/Account/Login" will be in Static Server Mode and that is why MudBlazor and its UI Controls will not work.

Try using HTML5 controls or in-built Blazor Input Controls such as InputText

Also inside OnInitializedAsync use

Input ??= new(); //This will re-initialize the Model in case of Null

Note: MudBlazor requires InteractiveServerRenderMode

Thank you - as noted in my question the following results in the page being displayed briefly then the page becomes blank and the text "Not found" is displayed. Same behavior if (prerender: false) is ommitted.

The text "Not found" does not exist anywhere in my .sln. Is this a dotnet error? There are no errors in the browser console and all network calls complete normally. Where does the error "Not found" come from? It is not MudBlazor as I have commented out all html on the page.

@code {
    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account")
        ? new InteractiveServerRenderMode(prerender: false) //results in "Not found"
    :    new InteractiveWebAssemblyRenderMode(prerender: false);

}

image

The "Not found" behavior can be reproduced as follows:

Create a new Blazor app with individual account auth, render mode = Auto

Change this line in server project App.razor:

 private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account")
     ? new InteractiveServerRenderMode(prerender: false)
     : InteractiveAuto;

Run the app and click Login on the nav menu Result:

image

F12 in the browser and refresh, note no 404 errors or console errors.

What is Blazor looking for that is not found?

This is how you can avoid the Not found issue that you're facing:

protected override void OnParametersSet()
{
if (HttpContext is null)
{
// If this code runs, we're currently rendering in interactive mode, so there is no HttpContext.
// The identity pages need to set cookies, so they require an HttpContext. To achieve this we
// must transition back from interactive mode to a server-rendered page.
NavigationManager.Refresh(forceReload: true);
}

Please note, that all the account pages share this logic (through the common layout), to force rendering on the server.

@mkArtakMSFT

Please note, that all the account pages share this logic (through the common layout), to force rendering on the server.

I'm sorry I don't understand this. If you follow the steps to reproduce that I provided above the account pages do not render.