Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EditForm and MudTextField not displaying error messages correctly in .NET 8 #8855

Open
1 of 2 tasks
mharre opened this issue May 2, 2024 · 8 comments
Open
1 of 2 tasks

Comments

@mharre
Copy link

mharre commented May 2, 2024

Bug type

Component

Component name

EditForm and MudTextField

What happened?

The MudTextField is not displaying validation correctly due to what appears to be the HTML being rendered in the wrong order on the page. Based on the docs, the divs are correctly nested in the paragraph element. In my .NET 8 app they are not.

Here is a screenshot of what it should look like based on the MudBlazor docs
Screenshot 2024-05-02 123300
The correct way should be as follows:

<p>
    <div>
        <div></div>
    </div>
</p>

In my app:
Screenshot 2024-05-02 123420

In the app it appears to be displayed as follows:

<p></p>
<div>
    <div></div>
</div>
<p></p>

I am using the standard mudblazor template install with .NET 8 and identity

code snippet:

<EditForm Model="Input" method="post" OnValidSubmit="LoginUser" FormName="login">
    <DataAnnotationsValidator />
				<ValidationSummary class="text-danger" role="alert" />
    <MudGrid>
        <MudItem xs="12" sm="7">
            <MudCard>
			<MudCardContent>
				    <MudTextField @bind-Value="Input.Email" name="Input.Email" aria-required="true" Placeholder="name@example.com" Label="email" For="@(() => Input.Email)" />
				    <MudTextField @bind-Value="Input.Password" name="Input.Password" aria-required="true" Placeholder="password" Label="password" For="@(() => Input.Password)" InputType="InputType.Password" />
				    <MudCardActions>
					  <MudButton ButtonType="ButtonType.Submit" Color="Color.Primary" FullWidth="true" Variant="Variant.Filled">Log In</MudButton>
					  </MudCardActions>
			</MudCardContent>
            </MudCard>
        </MudItem>
    </MudGrid>
</EditForm>

Expected behavior

Expected behavior would be to correctly style the input error text. Meaning smaller and red.

Here is what it should look like:
Screenshot 2024-05-02 125045

Here is what it looks like in my app:
Screenshot 2024-05-02 125200

Reproduction link

cannot submit this

Reproduction steps

  1. Create an EditForm with default ASP.NET core validation
  2. Use MudTextField for the form fields
  3. Add MudBlazor button that submits the form
  4. Click button
    ...

Relevant log output

N/A

Version (bug)

6.19.1

Version (working)

N/A

What browsers are you seeing the problem on?

Firefox, Chrome, Edge, Safari

On which operating systems are you experiencing the issue?

Windows

Pull Request

  • I would like to do a Pull Request

Code of Conduct

  • I agree to follow this project's Code of Conduct
@mharre mharre added the triage label May 2, 2024
@ScarletKuro
Copy link
Member

ScarletKuro commented May 2, 2024

I copy pasted your code into a MudBlazor .NET 8 (with Auto render mode) project and that's how it looks:
example1
Version: 6.19.1

Could it be a user code mistake?

@mharre
Copy link
Author

mharre commented May 3, 2024

I copy pasted your code into a MudBlazor .NET 8 (with Auto render mode) project and that's how it looks: example1 Version: 6.19.1

Could it be a user code mistake?

That is possible. All though I haven't made any changes to the code besides a few migrations. All I did was install mud templates, create a new .NET8 project Mud Blazor Web App with identity and both Server/ Web Assembly. This particular component is server side. I tried different render modes, still the same output.

This is the full razor page code

@page "/Account/Login"

@using System.ComponentModel.DataAnnotations
@using Microsoft.AspNetCore.Authentication
@using Microsoft.AspNetCore.Identity
@using MudBlazorWebApp2.Data

@inject SignInManager<ApplicationUser> SignInManager
@inject ILogger<Login> Logger
@inject NavigationManager NavigationManager
@inject IdentityRedirectManager RedirectManager

<PageTitle>Log in</PageTitle>

<h1>Log in</h1>
<div class="row">
    <div class="col-md-4">
        <section>
            <StatusMessage Message="@errorMessage" />
                <EditForm Model="Input" method="post" OnValidSubmit="LoginUser" FormName="login">
                    <DataAnnotationsValidator />
					<ValidationSummary class="text-danger" role="alert" />
                    <MudGrid>
                        <MudItem xs="12" sm="7">
                            <MudCard>
								<MudCardContent>
									<MudTextField @bind-Value="Input.Email" name="Input.Email" aria-required="true" Placeholder="name@example.com" Label="email" For="@(() => Input.Email)" />
									<MudTextField @bind-Value="Input.Password" name="Input.Password" aria-required="true" Placeholder="password" Label="password" For="@(() => Input.Password)" InputType="InputType.Password" />
									<MudCardActions>
										<MudButton ButtonType="ButtonType.Submit" Color="Color.Primary" FullWidth="true" Variant="Variant.Filled">Log In</MudButton>
									</MudCardActions>
								</MudCardContent>
                            </MudCard>
                        </MudItem>
                    </MudGrid>
                </EditForm>

        </section>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h3>Use another service to log in.</h3>
            <hr />
            <ExternalLoginPicker />
        </section>
    </div>
</div>

@code {
    private bool _processing = false;

    private string? errorMessage;
    bool isLoading = false;

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

    [SupplyParameterFromForm]
    private InputModel Input { get; set; } = new();

    [SupplyParameterFromQuery]
    private string? ReturnUrl { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (HttpMethods.IsGet(HttpContext.Request.Method))
        {
            // Clear the existing external cookie to ensure a clean login process
            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
        }
    }

    public async Task LoginUser()
    {
        _processing = true;
        await InvokeAsync(StateHasChanged);
        await Task.Delay(1);

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, set lockoutOnFailure: true

        var result = await SignInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
			_processing = false;
			await InvokeAsync(StateHasChanged);
			await Task.Delay(1);
            Logger.LogInformation("User logged in.");
			RedirectManager.RedirectTo(ReturnUrl);
        }
        else if (result.RequiresTwoFactor)
        {
			_processing = false;
            RedirectManager.RedirectTo(
                "Account/LoginWith2fa",
                new() { ["returnUrl"] = ReturnUrl, ["rememberMe"] = Input.RememberMe });
        }
        else if (result.IsLockedOut)
        {
			_processing = false;
            Logger.LogWarning("User account locked out.");
            RedirectManager.RedirectTo("Account/Lockout");
        }
        else
        {
			_processing = false;
            errorMessage = "Error: Invalid login attempt.";
        }
    }

    private sealed class InputModel
    {
        [Required]
        [EmailAddress]
        public string Email { get; set; } = "";

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; } = "";

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
    }
}

@truongdatnhan
Copy link
Contributor

truongdatnhan commented May 3, 2024

I get where you at. This problem appears in SSR (Server-side rendering) non-interactive mode.
Don't mistake it with Interactive SSR (old Blazor Server). Microsoft naming is very confusing.

If you look at Route.razor, you will see that paths starting with '/Account' will be rendered as null (SSR), not InteractiveServer.
Apparently MudBlazor state that It only support InteractiveServer, but I also think this case should work fine for SSR too.

@ScarletKuro
Copy link
Member

This is the full razor page code

Now from your full code I see that you are dealing with SSR(static) and this is expected. MudBlazor doesn't support this.
Solution: not to use MudBlazor components in identity pages.

only support InteractiveServe

Can you send a link where it says that we explicitly support only InteractiveServer? We support Interactive WebAssembly and Auto. Anything that has interactivity.

@truongdatnhan
Copy link
Contributor

truongdatnhan commented May 3, 2024

Update On this, as future investigate. TIL that you cannot nested <div> inside <p>.
https://stackoverflow.com/questions/44835895/div-nested-in-p

That's why the DOM refuse, but how can Blazor do this ?
ScarletKuro can you help me on this.

And I would like to make a PR to change <p> to <span> in MudInputControl.razor at line 18.
You can see: https://try.mudblazor.com/snippet/cuGIOpuHioAcTlXT

UPDATE:
After some tests, If you append HTML through javascript, it will not care. Blazor Interactive use JS to patch the dom so same behaviour.
NOTE: THIS IS NOT BLAZOR'S FAULT. I expect all other JS Frameworks to behave the same because they all use JS patching under the hood.

@mharre
Copy link
Author

mharre commented May 3, 2024

Thanks for the replies guys, interesting stuff. It has been a few years since I have needed to do any web / app development so I enjoy this learning process.

Is the only solution to this is to not use MudBlazor on an identity page?

If this isn't a Blazor issue I can gladly close this issue or have it closed for me 😄

@truongdatnhan
Copy link
Contributor

truongdatnhan commented May 4, 2024

Is the only solution to this is to not use MudBlazor on an identity page?

I'm afraid yes.
If you have limited Authentication pages that only require text field and select, then It prob okay.
However, I believe sooner or later you will need radio or checkbox (those require Interactive).
If you still want to get that MudBlazor look to match with the rest of the website, then prepare to write a lot of JS to change the CSS class and other stuff ☠️

Understand, this is not MudBlazor only. Others like Radzen, Fluent UI Blazor also require Interactive

@ScarletKuro
Copy link
Member

Is the only solution to this is to not use MudBlazor on an identity page?

Yes. And there are no plans to support static SSR.
I'll answer in advance if you're wondering why we don't support such an "important" feature - simply because static SSR doesn't support the interactivity that blazor offers. MudBlazor uses C# instead of JS as much as possible, and this is one of our core philosophies, and solving this problem will require using JS for many things.
There aren't many Blazor component libraries that support SSR flawlessly, except those that simply wrap boostrap/JS components.

@henon henon removed the triage label May 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants