首页 > 解决方案 > Blazor 组件双向数据绑定

问题描述

我以这种方式制作了 Blazor 组件:

<input bind-value-oninput="Title" />


@functions {

    [Parameter] string Title { get; set; }

    [Parameter] EventCallback<string> TitleChanged { get; set; }
}

家长:

@page "/fetchdata"

Parent:<input bind-value-oninput="Title" /> - Child:<Comp bind-Title="Title"></Comp>



@functions {
    string Title;

}

正如预期的那样,编辑父输入将其值传播到组件,但反之亦然,这是设计对吗?

我通过此编辑实现了双向行为:

<input bind-value-oninput="Title" />


@functions {
    string _Title { get; set; }
    [Parameter] string Title 

    {
        get { return _Title; }
        set
        {
            Console.WriteLine($"{value}->{_Title}");



            if (_Title != value)
            {
                _Title = value;
                TitleChanged.InvokeAsync(value);
            }


        }
    }

    [Parameter] EventCallback<string> TitleChanged { get; set; }
}

这是实现双向数据绑定的最佳实践吗?那么 TitleChanged 必须手动调用,对吧?是不是像WPF的PropertyChanged?

没有

     if (_Title != value)

代码进入循环,有人可以解释为什么?

标签: c#blazor2-way-object-databinding

解决方案


This is a two-way data binding between components. This behavior (or limitation) is by design. What you do, is the correct way to solve it, and I may venture to say, a best practice, at least according Steve Anderson, others may suggest different methods, such as a service class.

Some explanation of what is going on: When you apply a change to the parent component, the change event is triggered, the state of the component has changed, and the component should be re-rendered. To re-render the parent component, the StateHasChanged method is called by Blazor automatically. But when a state is changed on the child component, the parent component is ignorant of this, and we should notify it of this. Again, using event call backs for this is a good practice, to my mind...

Hope this helps....

Edit....

In general the parameter flow goes downwards, i.e., from parent to child, not in the other direction, because the rendering flow goes in that direction. That's why there isn't a way to pass parameters upstream (e.g., to a layout), because then there's be no single defined render order.

SteveSanderson

Marco: What I don't understand is why calling StateHasChanged on the parent causes the data to flow from the parent to the child and not the opposite.

First off, you don't manually call the StateHasChanged method. It is automatically called whenever the input event is triggered; that is after each keyboard key is pressed. And it is called to rerender the Component(parent). ( Note: It is tempting to think that the input tag is an HTML input tag element, but it is not so.)

As for why not the opposite: I think the quotation by SteveSanderson made it very clear.

When you want to pass a value (This is actually a parameter, but not an Attribute property) to the Parent Component, you need to use a different mechanism such as events (recommended by SteveSanderson; and work the same like in Angular). Below is sample code how it is performed. Important: When the state of the Parent Component is being changed from a child component, we should let the Parent Component to know that its state has changed and that it should rerender, by manually calling the StateHasChanged method. This is true when using the Action delegate, though I think that the EventCallback delegate is supposed to call StateHasChanged automatically.

Component A.cshtml

// Define a method in the parent component which will be called 
// from the child component when the user tap the button residing 
// in the child component. This method has a string parameter passed
// from the child component
public void GetValueFromChild(string value)
 {
        // Do somethig with value
  } 

Component B.cshtml

// When the user click the button the method GetValueFromChild
// defined on the parent component is called

<button class="btn" onclick=@(() => OnAddValue("some string value"))>Add</button>

    @functions
{
    // Define an Action delegate property which stores a reference
    // to A.GetValueFromChild
    // Parameters
    [Parameter] Action<string> OnAddValue{ get; set; }
}

A.cshtml

// Here (in the parent component) you place the child component and
// set its OnAddValue to the name of the method to be called
<B OnAddValue = "GetValueFromChild"></B> 

Please mark my answer as accepted if it helped you out Hope this helps...


推荐阅读