0

I created a customized input with label and some spans. (In the following, the spans are not shown, to keep the example simple.)

<div>
    <label for="length">length</label>
    <input type="number" id="length" @bind-value="@Length" min="100" max="500">
</div>
<span>@Length</span>

@code {
    private static double length = 300;
    private static int Length
    {
        get => (int)length;
        set => length = value;
    }
}

Since i need this customized input several times, I created a razor component LabeledInput.razor:

<div>
    <label for="@ID">@LabelText</label>
    <input type="number" id="@ID" @bind-value="@BindValue" min="@Min" max="@Max">
</div>

@code {
    [Parameter] public int BindValue { get; set; }
    [Parameter] public string ID { get; set; }
    [Parameter] public string LabelText { get; set; }
    [Parameter] public int Max { get; set; }
    [Parameter] public int Min { get; set; }
}

But when I use it as

[code from above]
<LabeledInput ID="length" LabelText="length2" Min="100" Max="500" BindValue="@Length"></LabeledInput>

clicking the spinner buttons of the LabeledInput does not update the <span>@Length</span> on the GUI. On the other hand, clicking the spinner buttons of the normal Input does update the span and also the LabeledInput. So, there is a binding but only in one direction.

What am I doing wrong?

Flippowitsch
  • 315
  • 3
  • 15

1 Answers1

2

What you want is to create two-way data-binding between the parent component and the child component. To achieve this, you'll have to create a Value property in your parent component like this:

private int Value { get; set; }

The Value property requires you to define a prameter property in the child component, like this... Let's call it Value

[Parameter]
Public int Value { get; set; }

In addition you'll have to create an event call back a prameter property like this:

[Parameter]
Public EventCallback<int> ValueChanged { get; set; }

The ValueChanged will contain the event call back that is to be called when the ValueChanged is triggered. Note that the EventCallback is of type int, meaning that you should pass an int value to the method (in our case it is the Value property defined in the parent component. Let's see how you call the child component from the parent component:

Index.razor

@page "/"
<Child @bind-Value="@Value" Min="0" Max="100" />
<div>@Value.ToString()</div>

@code
{
    private int Value { get; set; } = 10; // default value
}

Child.razor

<div>
    <input type="number" value="@Value" 
           @onchange="@((args) => ValueChanged.InvokeAsync(Convert.ToInt32(args.Value)) )" 
           min="@Min" max="@Max">
</div>

@code {
    [Parameter]
    public int Value { get; set; }

    [Parameter]
    public EventCallback<int> ValueChanged { get; set; }

    [Parameter] public int Max { get; set; }
    [Parameter] public int Min { get; set; }
}

Copy, test and then adapt...

Note that instead of providing the @onchange directive with a lambda expression as is done above, you can assign the name of a method, which you'll have to define, and from which you'll have to update the Value property

UPDATE:

Why can't I change the property name Value of the child component?

The rule goes like this: If you name the parameter property BindValue, like this:

[Parameter]
public int BindValue { get; set; }

Then, the EventCallback property should be named: BindValueChanged, like this:

[Parameter]
public EventCallback<int> BindValueChanged { get; set; }

And it is used in the parent component like this:

<Child @bind-BindValue="@Value" Min="0" Max="100" />

That is, you apply the @bind directive, plus a hyphen, plus the name of the parameter property defined in the child component. Its value: "@Value" is the name of the local property that is bound to the child component, and is updated with a new value passed from the child component, whenever you change the value in the input type="number" element. Get it?

enet
  • 41,195
  • 5
  • 76
  • 113
  • 1
    It works. Many thanks! Why can't I change the property name `Value` of the child component? I tried `BindValue` and it compiles but the child component is not visible. – Flippowitsch Jan 20 '22 at 16:10
  • @Flippowitsch, I've updated my answer... – enet Jan 20 '22 at 18:43
  • 1
    Well explained, thanks again. This way, I was even able to bind two values `BindValue` and `BindValue2` to `@Length` and `@Width`. – Flippowitsch Jan 21 '22 at 07:11
  • My example and your explanation ist for `int`s. I need `double`s now. I changed your default value from 10 to 10.1 and of course the type to double. I also changed everything in the Child component. It compiles but the input does not show the default value but is empty. What am i doing wrong? – Flippowitsch Jan 24 '22 at 12:38
  • Try adding step="0.01" to the input type="number" element. – enet Jan 24 '22 at 14:13
  • That did not work. Does it on your machine? – Flippowitsch Jan 24 '22 at 15:36
  • Hm, in Firefox the input is not empty. It is "10,1" (with a comma!). But clicking the upper spinner button changes the value to "10.11" for a second, what is then automatically updated to "1011". So, I think, I have an issue with decimal separators. I'll examine tomorrow... – Flippowitsch Jan 24 '22 at 16:38
  • "That did not work" Did you copy the code from the second answer? I've tested the code with Google Chrome, Brave, Firefox, Edge, and they all work as expected. – enet Jan 24 '22 at 19:03
  • Yes, I copied your code to a new project. – Flippowitsch Jan 25 '22 at 08:47
  • I created a new Question, because I wanted to provide more information and also the original question is answered. Please find the new question [here](https://stackoverflow.com/questions/70845705/cultureinfo-issue-with-two-way-binding) – Flippowitsch Jan 25 '22 at 08:48