Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Parameters with non-public accessors are not set #115

Open
bojankostic81 opened this issue Jul 29, 2022 · 9 comments
Open

Parameters with non-public accessors are not set #115

bojankostic81 opened this issue Jul 29, 2022 · 9 comments
Labels
type: bug Something isn't working

Comments

@bojankostic81
Copy link

Question

Hi,
How can I get AuthenticationState in ViewModel? I'm trying with:

[CascadingParameter] Task AuthenticationState { get; set; }

but it is always null.

Thanks.

Code sample

No response

Version

6.0.6

Are you using Blazor WASM or Blazor Server?

Blazor WASM

@bojankostic81 bojankostic81 added status: triage Needs to be triaged type: question Questions about the library labels Jul 29, 2022
@klemmchr
Copy link
Owner

Cascading parameters are supported. Could you create a minimal reproduction to show the exact issue? Keep in mind that you need to declare such parameters in both the component and the view model in order to make this work. Have a look at the Readme for some examples regarding parameters.

@klemmchr klemmchr removed the status: triage Needs to be triaged label Jul 29, 2022
@bojankostic81
Copy link
Author

bojankostic81 commented Jul 30, 2022

Hi,
Here is my component code:

@attribute [Authorize]
@layout MainLayout
@using System.Collections.ObjectModel
@inherits MvvmComponentBase<TeamViewModel>

<div id="main-content">
    @if (@BindingContext.DataSource == null)
    {
        <Placeholder />
    }
    else
    {
        <TeamMembers [email protected]/>
    }
</div>

@code{
    [CascadingParameter] Task<AuthenticationState> authenticationState { get; set; }
    [Parameter] public ObservableCollection<User> DataSource { get; set; }
}

and here is my ViewModel code:

public class TeamViewModel : ViewModelBase
{
    private readonly ITeamService _teamService;
    private readonly IJSRuntime _jsRuntime;

    [CascadingParameter] Task<AuthenticationState> authenticationState { get; set; }
    [Parameter] public ObservableCollection<User> DataSource { get; set; }
    public MetaData Paging { get; set; }

    public TeamViewModel(ITeamService teamService, IErrorHelper errorHelper, IJSRuntime jsRuntime)
    {
        _teamService = teamService;
        _jsRuntime = jsRuntime;
    }

    public override async Task OnInitializedAsync()
    {
        var a = authenticationState.Result;
        //var response = await _teamService.GetTeamAsync(new QueryParameters() { AccountId = 2 });
        //DataSource = new ObservableCollection<User>(response.Items);
        //Paging = response.MetaData;
    }
}

In this line of code: var a = authenticationState.Result, authenticationState is null.

@klemmchr
Copy link
Owner

Can you confirm that the property has a value in the component itself?

@bojankostic81
Copy link
Author

bojankostic81 commented Jul 31, 2022

I have added initialization in my component, like this:


@attribute [Authorize]
@layout MainLayout
@using System.Collections.ObjectModel
@inherits MvvmComponentBase<TeamViewModel>

<div id="main-content">
    @if (@BindingContext.DataSource == null)
    {
        <Placeholder />
    }
    else
    {
        <TeamMembers [email protected]/>
    }
</div>

@code{
    [CascadingParameter] Task<AuthenticationState> authenticationState { get; set; }
    [Parameter] public ObservableCollection<User> DataSource { get; set; }

    protected override void OnInitialized()
    {       
        var auth = authenticationState.Result;
    }

}

In component, I receive result from authenticationState.Result, however, now my BindingContext is null. I'm guessing that it is because I have OnInitialize in both component and ViewModel. When I leave OnInitialize methot only on component it works fine, but I need it inside ViewModel. Do you have any idea how to resolve this.

@klemmchr
Copy link
Owner

klemmchr commented Jul 31, 2022

If you override OnInitialized() in your component you need to call base.OnInitialized(). Otherwise the binding will be lost. You don't need to override it anyways, simply setting a breakpoint into the setter of the authenticationState property should be enough.

I would still assume that your authentication state is simply null in the first place. Please review the docs and ensure that you properly set up your router.

@hrannzo
Copy link

hrannzo commented Aug 18, 2022

@bojankostic81 try changing the visibility to public (on both the component and view model), then I believe it should work.
[CascadingParameter] public Task<AuthenticationState> authenticationState { get; set; }

@klemmchr
Copy link
Owner

@hrannzo good catch, does parameter injection into components work without a public setter? If so, such a use case should be supported by this library.

@hrannzo
Copy link

hrannzo commented Aug 18, 2022

@klemmchr It seems that a property can be private but it must have a setter defined when receiving a cascading value. The setter can be private and init. If the setter is missing then an exception is thrown from the belly of the Blazor.

CascadingComponent.razor

Name1=@Name1
<br/>
Name2=@Name2
<br/>
Name3=@Name3
<br/>
Name4=@Name4
<br/>
Name5=@Name5

@code {

    [CascadingParameter(Name = nameof(Name1))]
    public string? Name1 { get; init; }

    [CascadingParameter(Name = nameof(Name2))]
    public string? Name2 { get; private set; }
    
    [CascadingParameter(Name = nameof(Name3))]
    private string? Name3 { get; init; }

    [CascadingParameter(Name = nameof(Name4))]
    private string? Name4 { get; set; }

    // Attribute commented out since it would crash the app
    // Runtime error: Cannot provide a value for property 'Name' on type '...' because the property has no setter.
    //[CascadingParameter(Name = nameof(Name5))]
    public string? Name5 { get; }

    // Does not compile: BL0004 - Component parameter should be public
    // [Parameter] private string Name6 { get; set; }
}

WrapperComponent.razor

<CascadingValue Value="@("Hello")" Name="Name1">
    <CascadingValue Value="@("World")" Name="Name2">
        <CascadingValue Value="@("Foo")" Name="Name3">
            <CascadingValue Value="@("Bar")" Name="Name4">
                <CascadingValue Value="@("Crash")" Name="Name5">
                    <CascadingComponent></CascadingComponent>
                </CascadingValue>
            </CascadingValue>
        </CascadingValue>
    </CascadingValue>
</CascadingValue>

Rendered output:
image

@klemmchr
Copy link
Owner

klemmchr commented Aug 19, 2022

@hrannzo thanks for the investigation, this actually is a bug in the library

@klemmchr klemmchr added type: bug Something isn't working and removed type: question Questions about the library labels Aug 19, 2022
@klemmchr klemmchr changed the title Get AuthenticationState in view model Parameters with non-public accessors are not set Aug 19, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants