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

[Question] How do we set a storage item on a button click? #251

Open
MrYossu opened this issue Nov 6, 2024 · 3 comments
Open

[Question] How do we set a storage item on a button click? #251

MrYossu opened this issue Nov 6, 2024 · 3 comments
Labels
Question Question about this project Triage Issue needs to be triaged

Comments

@MrYossu
Copy link

MrYossu commented Nov 6, 2024

I'm using the .NET8 Blazor web app template, with the Identity stuff included.

I would like to set a local storage item when the user logs in, so added a single line to the Login.razor method that is called when the form is submitted...

  public async Task LoginUser() {
    // This doesn't count login failures towards account lockout
    // To enable password failures to trigger account lockout, set lockoutOnFailure: true
    SignInResult result = await SignInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
    if (result.Succeeded) {
      await LocalStorage.SetItemAsStringAsync("Greeting", Input.Salutation);
      Logger.LogInformation("User logged in.");
      RedirectManager.RedirectTo(ReturnUrl);
    } else if (result.RequiresTwoFactor) {
      RedirectManager.RedirectTo(
        "Account/LoginWith2fa",
        new() { ["returnUrl"] = ReturnUrl, ["rememberMe"] = Input.RememberMe });
    } else if (result.IsLockedOut) {
      Logger.LogWarning("User account locked out.");
      RedirectManager.RedirectTo("Account/Lockout");
    } else {
      _errorMessage = "Error: Invalid login attempt.";
    }
  }

In this case, I have added a Salutation property to the InputModel, and want to store that in local storage.

However, when this is run, I get an exception....

InvalidOperationException: JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method

OK, so I added the following at the top of the file...

@rendermode @(new InteractiveServerRenderMode(prerender: false))

However, that gives a different exception...

System.InvalidOperationException: Headers are read-only, response has already started

I'm a bit stuck now. Anyone able to explain how I do this?

Thanks

@MrYossu MrYossu added Question Question about this project Triage Issue needs to be triaged labels Nov 6, 2024
@amabilee
Copy link

amabilee commented Nov 6, 2024

Hey there!

  1. InvalidOperationException: JavaScript interop calls cannot be issued at this time, happens because JavaScript interop calls are not allowed during static rendering. To address this, you correctly tried to change the render mode to InteractiveServerRenderMode

  2. System.InvalidOperationException: Headers are read-only, response has already started, occurs because the Identity pages require access to the underlying HttpContext, which is not available in interactive mode

To resolve these issues, you can use the OnAfterRenderAsync lifecycle method to perform JavaScript interop calls after the component has been rendered

@MrYossu
Copy link
Author

MrYossu commented Nov 6, 2024

@amabilee Thanks for the reply, but I need to make the calls when the form is submitted. As far as I understand it, OnAfterRenderAsync is a lifecycle event that happens by itself when the component is created. The user will fill in and submit the form long after that has ended.

I can't make my call in OnAfterRenderAsync, as I won't have the data to store in local storage (the fixed value in the code sample was perhaps a bit misleading, I'm actually storing user-related info).

Thanks again, any further advice?

@amabilee
Copy link

amabilee commented Nov 7, 2024

You can handle the JavaScript interop after the form submission by performing the interop call within a callback. You can achieve this by using a helper method or a service to interact with JavaScript.

This will handle local storage interactions:

window.localStorageHelper = {
        setItem: function (key, value) {
                localStorage.setItem(key, value);
        }
};

Then inject IJSRuntime into your component and use it to set the local storage item after the form submission,

Make sure this JavaScript file is referenced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question Question about this project Triage Issue needs to be triaged
Projects
None yet
Development

No branches or pull requests

2 participants