简体   繁体   English

Blazor中class的双向组件参数绑定?

[英]Two-Way Component Parameter Binding on a class in Blazor?

I have a class MealsQueryInputs that I would like to use as a component parameter with two-way binding capabilities.我有一个 class MealsQueryInputs ,我想将其用作具有双向绑定功能的组件参数。

All of the demos and sample code I can find are using built-in primitive types and never a class. I can get the MS demos to work but I cannot get binding to a class to work.我能找到的所有演示和示例代码都使用内置原始类型,而不是 class。我可以让MS 演示工作,但我无法绑定到 class 工作。 Is it even possible to do this?甚至可以这样做吗?

My component FilterSortOptions.razor :我的组件FilterSortOptions.razor

using WhatIsForDinner.Shared.Models

<MudCheckBox Checked="@QueryInputs.Favorite" 
             Color="Color.Inherit" 
             CheckedIcon="@Icons.Material.Filled.Favorite" 
             UncheckedIcon="@Icons.Material.Filled.FavoriteBorder"
             T="bool"/>
<MudRating SelectedValue="@QueryInputs.Rating"/>
<MudButton OnClick="@(async () => await OnPropertyChanged())">Apply</MudButton>

@code {
    [Parameter]
    public MealsQueryInputs QueryInputs { get; set; }

    [Parameter]
    public EventCallback<MealsQueryInputs> QueryInputsChanged { get; set; }

    private async Task OnPropertyChanged()
    {
        await QueryInputsChanged.InvokeAsync(QueryInputs);
    }
}

As MrC said, you should avoid directly binding to the data being supplied as a parameter.正如 MrC 所说,您应该避免直接绑定到作为参数提供的数据。

Here is a simple working sample (not MudBlazor) to show the concept这是一个简单的工作示例(不是 MudBlazor)来展示这个概念

https://blazorrepl.telerik.com/QQEnQjaO54LY3MYK35 https://blazorrepl.telerik.com/QQEnQjaO54LY3MYK35

You bind to a local variable/property and try not to modify the incoming data directly.您绑定到局部变量/属性,并尽量不要直接修改传入的数据。

MyComponent我的组件


<h1>MyComponent</h1>

<label for="choice">Choose</label>
<input id="choice" type="checkbox" @bind-value=localValue  />

@code
{
    bool localValue
    {
        get => Data.SomeChoice;
        set {
            if (value != localValue)
            {
                localData = Data with { SomeChoice = value };
                InvokeAsync(ValueChanged);
            }
        }
    }
    ComplexObject localData;
    [Parameter] public ComplexObject Data { get; set; }
    [Parameter] public EventCallback<ComplexObject> DataChanged { get; set; }

    Task ValueChanged() => DataChanged.InvokeAsync(localData);
}

ComplexObject复杂对象

public record ComplexObject(bool SomeChoice, string SomeText);

Main主要的

@code
{
    ComplexObject data = new(false,"");
}

<MyComponent @bind-Data=data />

You have chosen @data.SomeChoice

Here is how you can bind class objects to a custom razor component以下是如何将 class 对象绑定到自定义 razor 组件

This is FilterSortOptions component这是 FilterSortOptions 组件

<div>
    <label>Rating:</label>
    <input type="text" value=@QueryInputs.Rating @oninput=@(val=> {
           QueryInputs.Rating=val.Value.ToString();
           QueryInputsChanged.InvokeAsync(QueryInputs);
           }) />
</div>

<div>
    <label>Favourite:</label>
    <input type="checkbox" value=@QueryInputs.Rating @onchange=@(val=> {
           QueryInputs.Favourite=(bool)val.Value;
           QueryInputsChanged.InvokeAsync(QueryInputs);
           }) />
</div>

    @code {
    
        [Parameter]
        public MealsQueryInputs QueryInputs { get; set; }
    
        [Parameter]
        public EventCallback<MealsQueryInputs> QueryInputsChanged { get; set; }
    
    }

This is the model to bind, for simplicity Rating is is string type这里绑定的是model,为了简单起见Rating是string类型

public class MealsQueryInputs
{
    public bool Favourite { get; set; } = false;
    public string Rating { get; set; } = "0";
}

Here is the razor page这是razor页面

<h3>Rating: @QueryInputs.Rating</h3>
<h3>Favourite: @QueryInputs.Favourite</h3>

<FilterSortOptions @bind-QueryInputs=@QueryInputs></FilterSortOptions>

@code {
    public MealsQueryInputs QueryInputs = new();
}

Updated Answer更新的答案

Firstly, if your using an object then you are passing around references to the same object. So when you update the object in the sub-component, you're updating the same object the parent is using.首先,如果您使用 object,那么您将传递对相同 object 的引用。因此,当您更新子组件中的 object 时,您将更新父组件正在使用的相同 object。 You don't need to pass the object back in the callback unless you create a noew copy of it.您不需要在回调中传回 object,除非您创建它的新副本。

Secondly, your not binding the mud controls to the object.其次,您没有将泥浆控件绑定到 object。

Let's look at your code:让我们看看你的代码:

<MudCheckBox Checked="@QueryInputs.Favorite" 
             Color="Color.Inherit" 
             CheckedIcon="@Icons.Material.Filled.Favorite" 
             UncheckedIcon="@Icons.Material.Filled.FavoriteBorder"
             T="bool"/>

Checked="@QueryInputs.Favorite" doesn't bind the control to the field. Checked="@QueryInputs.Favorite"不会将控件绑定到字段。 It just sets the initial value.它只是设置初始值。

I think (I don't use Mudblazor and it's a little different from standard Blazor Form Controls) you need to do this:我认为(我不使用 Mudblazor,它与标准的 Blazor 表单控件有点不同)你需要这样做:

<MudCheckBox @bind-Checked="@QueryInputs.Favorite"></MudCheckBox>

The same is true for MudRating . MudRating也是如此。

    <MudRating @bind-SelectedValue="@QueryInputs.Rating" />

Then the button:然后是按钮:

<MudButton OnClick="@(async () => await OnPropertyChanged())">Apply</MudButton>

can be simplified to this.可以简化成这样。 You're wrapping an async method within an async method.您将异步方法包装在异步方法中。

<MudButton OnClick="OnPropertyChanged">Apply</MudButton>
// or
<MudButton OnClick="() => OnPropertyChanged()">Apply</MudButton>

Original Answer原始答案

There are a couple of issues here:这里有几个问题:

  1. QueryInputs is a Parameter and therefore should never be modified by the code within the component. QueryInputs是一个Parameter ,因此永远不应被组件内的代码修改。 You end up with a mismatch between what the Renderer thinks the value is and what it actually is.您最终会发现渲染器认为的值与实际值不匹配。

  2. When the parent component renders it will always cause a re-render of any component that is passed a class as a parameter.当父组件呈现时,它总是会导致重新呈现作为参数传递 class 的任何组件。 The Renderer has no way of telling if a class has been modified, so it applies the heavy handed solution - call SetParametersAsync on the component.渲染器无法判断 class 是否已被修改,因此它应用了笨拙的解决方案 - 在组件上调用SetParametersAsync

A solution is to use a view service to hold the data and events to notify changes.一种解决方案是使用视图服务来保存数据和事件以通知更改。 One version of the truth.一个版本的真相。 Search "Blazor Notification Pattern" for examples of how to implement this.搜索“Blazor Notification Pattern”以获取有关如何实现此功能的示例。 I'll post some code if you can't find what you want.如果你找不到你想要的,我会发布一些代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM