8

I'm playing around with the custom template in Blazor and I'm trying to find to a way to two-way bind a CascadingValue or achieve something similar. Right now I have the following template.

@if (PopupVisible)
{
    <DxPopup>
        <HeaderTemplate>
            <h4 class="modal-title">@HeaderText</h4>
            <button type="button" class="close" @onclick="@UpdatePopupVisible">&times;</button>
        </HeaderTemplate>
        <ChildContent>
            <div class="modal-body">
                <div class="container-fluid">
                  @bodyContent
                </div>
            </div>
            <div class="modal-footer">
                @footerContent
                <button class="btn btn-secondary" @onclick="UpdatePopupVisible">Cancel</button>
            </div>
        </ChildContent>
    </DxPopup>
}

@code {
    [CascadingParameter] public bool PopupVisible { get; set; }
    [CascadingParameter] public EventCallback<bool> PopupVisibleChanged { get; set; }

    [Parameter] public RenderFragment HeaderText { get; set; }
    [Parameter] public RenderFragment footerContent { get; set; }
    [Parameter] public RenderFragment bodyContent { get; set; }

    private async Task UpdatePopupVisible()
    {
        PopupVisible = false;
        await PopupVisibleChanged.InvokeAsync(PopupVisible);
    }
}

Then I have a component that implements this template(child), and then I have that component called with a button press(parent). What I want to know is if there is a way to bind the PopupVisible parameter from the parent without having to bind it the child and having the child pass it to the template. I haven't found a way to two-way bind a cascading parameter but if possible I think that would be the best way to do so. Outside of that, I'm not sure if there is another way or I'm going to have to go with my current idea of passing the value.

Amal K
  • 4,359
  • 2
  • 22
  • 44
Mylies
  • 396
  • 4
  • 18

2 Answers2

8

You can't do two-way binding with cascading parameters. Cascading means flowing downstream, from parent to child, and not the other way around.

I'm not sure I understand your question...however, if you wish to pass a value from a parent component and back; you can do the following: Note: This is a two-way Component data binding

Child Component

@code
{
    private bool visible;
    [Parameter]
    public bool PopupVisible
    {
        get { return visible }
        set
        {
            if (visible != value)
            {
                visible = value;

            }
        }
    } 

   [Parameter] public EventCallback<bool> PopupVisibleChanged { get; set; }
   // Invoke the EventCallback to update the parent component' private field visible with the new value.

   private Task UpdatePopupVisible()
    {
        PopupVisible = false;
        return PopupVisibleChanged.InvokeAsync(PopupVisible);
    }
}

Usage

@page "/"

<DxPopup @bind-PopupVisible="visible" />

@code {
    private bool visible;
}

Note: If you need some explanation, and believe that I did not answer your question, don't hesitate to tell me, but please take the time to phrase your questions... I could not completely understand questions.

enet
  • 41,195
  • 5
  • 76
  • 113
  • Thanks for answering my question! I understand two way data binding but the problem is I want to bind that is a child of a child. I have it set up as Parent->Child->template and I want to bind popUpVisible between the Parent and the template, ideally without adding anything extra in child. – Mylies Dec 18 '19 at 19:25
  • 1
    You can pass the value of popUpVisible from the parent to the templated component by using the CascadingValue component on the parent, and defining a CascadingParameter property named PopupVisible on the templated component. But you cannot update the passed parameter from the templated component with cascading values. Instead you can define a service which can be accessed from the parent component (inject the service into the parent component, assign a value to the boolean property), – enet Dec 18 '19 at 19:48
  • 1
    and from the templated component( inject the service into your component, read the value set in the parent component, assign a new value, and now the parent component can access the new value, using the injected service). You should add the service to the DI container in the Startup.ConfigureServices method – enet Dec 18 '19 at 19:48
3

what you can do is, Cascade the parent component and in the child component, access the parent Property you want to change like this:

Parent:

<CascadingValue Value="this">
    <Child />
</CascadingValue>

Child:

[CascadingParameter]
public Parent Parent { get; set; }

.....

private void ChangeParentProperty()
{
    Parent.Property = ....; 
}

Any doubt feel free to ask.

MarchalPT
  • 1,268
  • 9
  • 28
  • As of 2023, the compiler emits warnings about setting component parameters through code (rather than in markup with binding), though in practice I have done it and it seems ok. I suppose there may be some event/repaint triggers that may not be properly fired if you set parameters this way. Just be aware that there are likely some limitations to this approach that aren't immediately apparent. – Richard Hauer Jun 14 '23 at 00:01
  • Hi @RichardHauer, the warning have to do with .Net 7 and the "nullable reference types". It has nothing to do with Cascading Parameters that are a valid option in Blazor. In order to clear the warning in Blazor you can mark the object type with "?" like this "Parent?" or if you are sure it will never be null, add in from of the { get; set; } this "= default!;". You can see this on their official documentation: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-7.0 Or in this answer: https://stackoverflow.com/a/73465000/12824729 – MarchalPT Jun 14 '23 at 07:58
  • the warning is actually `BL0005: Component parameter 'Property' should not be set outside of its component.` where 'Property' is the name of the property that you are setting. This warning comes up (for me) whenever you set a property that is marked with the [Parameter] attribute. So it's not the 'Parent' in your example that's the issue here, but the fact that the 'Property' of the parent is (in the case of OP's question) itself marked as a component parameter. – Richard Hauer Jun 14 '23 at 11:26
  • @RichardHauer I get what you say now, just read your comment and was out of context, definitly you should never set a component parameter property outside of its component – MarchalPT Jun 14 '23 at 12:40