28

I have a server-side blazor client and I'm trying to modify the MainLayout razor page by having a Login check. I'm currently using Blazored for localstorage saving, and I'm currently using to see if a token is saved to see if user is logged in, however I'm not sure how I translate this in the if statement in razor page because it wants async method.

My login check is pretty simple as shown below.

public async Task<bool> IsLoggedIn()
{
    return await m_localStorage.ContainKeyAsync("token").ConfigureAwait(false);
}

In my Razor page I'm doing this statement check - which obvious doesn't work as there's no async modifier

@if (!await AppState.IsLoggedIn()) //Requires async modifier
{
    <a href="Login" target="_blank">Login</a>
}

I've also tried doing it using the .Result property, but this results in an exception thrown: (System.AggregateException: 'Information: Executed an implicit handler method, returned result Microsoft.AspNetC)' with an inner-exception -> NullReferenceException: Object reference not set to an instance of an object.

But from what I can see AppState is injected correctly and the local storage seems to be injected correctly in AppState.

@if (!AppState.IsLoggedIn().Result)
{
    <a href="Login" target="_blank">Login</a>
}

So my question is what is the correct way to approach this, is there a way to execute async methods in razor pages?

4 Answers 4

20

is there a way to execute async methods in razor pages?

No, there isn't a way to use await in a Razor component. This is because you can't do async work as part of the rendering of the component.

Incidentally, the local storage mechanism provided by the Blazor team supports data protection, and is recommended for use by Steve Sanderson.

Note: The async Lifecycle methods of the component are where async work is done, and thus you can design your code accordingly, as for instance, calling AppState.IsLoggedIn() from OnInitializedAsync, and assigning the returned value to a local variable which can be accessed from your views.

5
  • Which localstorage mechanism, I could only find third party libraries to support local storage i.e blazored. Furthermore I can't call it from OnINit method as its server-side blazor and as such js interlop isn't ready at that point
    – A.A
    Commented Oct 13, 2019 at 11:23
  • Calling on AfterRenderAsync seems to work but I had to call InvokeAsync() on StateChanged or exception will be thrown
    – A.A
    Commented Oct 13, 2019 at 12:19
  • Yes,OnAfterRenderAsync method is the appropriate place as this method is not called when your app is pre-rendered. And you must call the StateHasChanged method to rerender your component with the new data
    – user12207343
    Commented Oct 13, 2019 at 13:55
  • See this article for local storage mechanism from Blazor team: learn.microsoft.com/en-us/aspnet/core/blazor/…
    – user12207343
    Commented Oct 13, 2019 at 13:59
  • There is a way actually, check my answer Commented May 9, 2020 at 4:47
4

AsyncComponent.razor

    @typeparam TResult
    @typeparam TInput
    @if (Result != null)
    {
        @DataReadyFragment(Result)
    }
    else if (DataMissingFragment != null)
    {
        @DataMissingFragment
    }
    @code {
        [Parameter] public RenderFragment<TResult> DataReadyFragment { get; set; }
        [Parameter] public RenderFragment DataMissingFragment { get; set; }
        [Parameter] public Func<TInput, Task<TResult>> AsyncOperation { get; set; }
        [Parameter] public TInput Input { get; set; }
        TResult Result { get; set; }

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if(firstRender)
AsyncOperation.Invoke(Input).ContinueWith(t => { Result = t.Result; InvokeAsync(StateHasChanged); });
        }
    }

Usage

<AsyncComponent TResult="User" TInput="string" Input="Pa$$W0rd" AsyncOperation="@AsyncMethodName">
    <DataReadyFragment Context="result">
        @if(result.IsLoggedIn)
         {
           <h3>Logged-In , Username:@result.Name</h3>
         }
         else
         {
          <h3>Wrong Password</h3>
         }
    </DataReadyFragment>
    <DataMissingFragment>
        <h3>Please Login :)</h3>
    </DataMissingFragment>
</AsyncComponent>
2
  • 1
    Good job. But i had to change a little bit: if (firstRender) { await AsyncOperation.Invoke(Input).ContinueWith(t => { Result = t.Result; InvokeAsync(StateHasChanged); }); } Otherwize it loops endless....
    – Bronzato
    Commented May 5, 2020 at 13:07
  • 1
    @Bronzato yes you right , i forgot to add that line of code. I’ll update my answer Commented May 9, 2020 at 4:44
4

Based on LazZiya example, worked for me. In my case an event was not async as supported by a component, but the call I had required await. Using this example I could return data from the call based on a model.

public string Name => Task.Run(() => Service.GetNameAsync()).GetAwaiter().GetResult();

I ended up with this and it worked like a charm.

Task<AssessmentResponsesModel> task = Task.Run(() => _IAssessmentResponse.CreateAsync(APIPathD, argsItem, token).GetAwaiter().GetResult());
2
  • 3
    Blows up at runtime in WASM. Commented Sep 1, 2021 at 17:04
  • Can't comment regarding WASM, this works server-side Blazor which was specified in the question. With WASM some things have a different solution.
    – WebGeek
    Commented Sep 2, 2021 at 18:29
-2

Call your C# method in Blazor Component's HTML

@if (IsBookmarked(BlogID).GetAwaiter().GetResult() == true)
{
                                                            
}
2
  • Can someone clarify if this is OK to use?
    – Ksdmg
    Commented Jun 21, 2023 at 13:41
  • 1
    It compiles, but fails at runtime, as expected. Commented Dec 29, 2023 at 21:41

Not the answer you're looking for? Browse other questions tagged or ask your own question.