MvvmBlazor icon indicating copy to clipboard operation
MvvmBlazor copied to clipboard

Parameters with non-public accessors are not set

Open bojankostic81 opened this issue 2 years ago • 9 comments

Question

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

[CascadingParameter] Task<AuthenticationState> 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 avatar Jul 29 '22 14:07 bojankostic81

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 avatar Jul 29 '22 16:07 klemmchr

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.

bojankostic81 avatar Jul 30 '22 12:07 bojankostic81

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

klemmchr avatar Jul 30 '22 19:07 klemmchr

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.

bojankostic81 avatar Jul 31 '22 04:07 bojankostic81

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.

klemmchr avatar Jul 31 '22 12:07 klemmchr

@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; }

hrannzo avatar Aug 18 '22 10:08 hrannzo

@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.

klemmchr avatar Aug 18 '22 17:08 klemmchr

@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

hrannzo avatar Aug 18 '22 21:08 hrannzo

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

klemmchr avatar Aug 19 '22 00:08 klemmchr