简体   繁体   中英

Correct way to mutate a component property in blazor

I have two components, Child.razor and Parent.razor .

The Child.razor HTML:

<input type="text" value="@Text" />

The Child.razor C#:

[Parameter] public string Text { get; set; }

And the Parent.razor HTML:

<Child @ref="child_1" />
<Child @ref="child_2" />
<Child @ref="child_3" />

Parent.razor C#:

Child child_1;
Child child_2;
Child child_3;

void SetText(Child item, string text)
{
    item.Text = text;
}

I get a warning on the item.Text = text :

Warning BL0005: Component parameter 'Text' should not be set outside of its component.

After some googling, I found this question: BL0005 - external parameter usage - why is a warning for that?

The answer is great, but it does not provide an alternative (the linked content on github is also not very helpful).

What is the correct way to mutate a component parameter from the parent?

EDIT

To clarify a bit more: I know I could use binding, but I need to be able to change the value inside the SetText method, passing the Child I want to mutate as a parameter. The problem with binding is that the variable is not tied with the component. In other words: with the referente to the Child, I am not able to know which binding string I should set.

For example:

<Child @ref="child_1" @Text="binding_1" />
<Child @ref="child_2" @Text="binding_2"/>
<Child @ref="child_3" @Text="binding_3"/>

Parent.razor C#:

Child child_1;
Child child_2;
Child child_3;

string binding_1;
string binding_2;
string binding_3;

void SetText(Child item, string text)
{
     // which binding string should I set?
}

I can visualize some funky code, creating a Dictionary<Child, string> to correlate the Component to the binding string, or something like that, but... really?

You can define a property of type Child in the parent component pass a reference of the parent component (this) to the child component property of Parent type. Now the child component hold a reference to the parent component, and it can add itself (again using this) to the parent. Now you have a reference to the child component, and you can set its Text property to something interesting. I hope I'm clear, if not I'll post code to reflect this. The following code works...

Child.razor

<input type="text" value="@Text" />

@code
{
    [Parameter] public string Text { get; set; }
    public void SetText(string text)
    {
        Text = text;
        StateHasChanged();
    }
    [ParameterAttribute] public Parent Parent { get; set; }
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            Parent.AddToParent(this);
        }
    }
}

Note that the original component parameter Text's value assigned in the parent component ( Text="Some Text" ) is not visible in the text box because immediately after the Parent's SetText method calls the Child's SetText method, which in return assign the value passed to it to the Text property, and thus the value seen in the text box is "new text"

Parent.Razor

<Child Parent="this" Text="Some Text" />

@code{
    public void AddToParent(Child child)
    {
        string text = "new text";
        SetText(child, text);
    }

    void SetText(Child item, string text)
    {
        // which binding string should I set?
        item.SetText(text);

    }
}

Usage

<Parent />

Мaybe it can help:

  1. declare the global delegate type
    public delegate void OnInput(string v);
  1. Child
    <input type="text" @bind-value="@Value" />
    
    @code {
        [Parameter]
        public OnInput oninput { get; set; }
        [Parameter]
        public string Value
        {
            get
            {
                return v;
            }
            set
            {
                v = value;
                if (oninput!=null) oninput(value);
            }
        }
        private string v;
    }
  1. Parent
    <Child Value=@binding_1 OnInput=@((__v)=>{binding_1=__v;})/>
    <Child Value=@binding_2 OnInput=@((__v)=>{binding_2=__v;})/>
    <Child Value=@binding_3 OnInput=@((__v)=>{binding_3=__v;})/>
    @code {
        public string binding_1=new string("default value 1");
        public string binding_2=new string("default value 2");
        public string binding_3=new string("default value 3");
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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