Josh
Josh

Reputation: 685

Is there a way to do two-way binding in blazor inputtext dynamically

I am trying to create a reusable control. I have figured out how to handle the read-only scenarios using reflections with @typeof(TItem).GetProperty(col.PropertyName).GetValue(item)

And now I want to do the same with two-way binding Like <InputText @bind-Value="item.{select a property using the PropertyName value in ColumnDefinition}</InputText>"

Is there a way to select the item's property using a string?

Thanks!

More references below:

@typeparam TItem
<EditForm>
    <table>
        <thead>
            <tr>
                <Virtualize Items="ColumnDefinitions" Context="col">
                    <th>
                        @col.Caption
                    </th>
                </Virtualize>
            </tr>
        </thead>
        <tbody>
            <Virtualize Items="Items" Context="item">
                <tr>
                    <Virtualize Items="ColumnDefinitions" Context="col">
                        <td>
                            @typeof(TItem).GetProperty(col.PropertyName).GetValue(item)
                            <InputText @bind-Value=""></InputText>
                        </td>
                    </Virtualize>
                </tr>
            </Virtualize>
        </tbody>
    </table>
</EditForm>


@code {
    [Parameter] public List<ColumnDefinition> ColumnDefinitions { get; set; }
    [Parameter] public List<TItem> Items { get; set; }
}

    public class ColumnDefinition
    {
        public short Order { get; set; }
        public string Caption { get; set; }
        public bool IsReadOnly { get; set; } = true;
        public InputTypeEnum InputType { get; set; } = InputTypeEnum.Text;
        public string PropertyName { get; set; }
    }

Upvotes: 1

Views: 3887

Answers (3)

Josh
Josh

Reputation: 685

<InputText> is different from <input>

<InputText> requires Value, ValueChanged and ValueExpression.

@typeparam TItem
@using System.Linq.Expressions

<InputText Value="@Value"
           ValueChanged="@ValueChanged"
           ValueExpression="@ValueExpression"
           class="form-control">
</InputText>

@code {
    [Parameter] public TItem Item { get; set; }
    [Parameter] public string PropertyName { get; set; }
    private string Value;
    private EventCallback<string> ValueChanged;
    private Expression<Func<string>> ValueExpression;

    protected override void OnInitialized()
    {
        Value = typeof(TItem).GetProperty(PropertyName).GetValue(Item).ToString();
        ValueChanged = Microsoft.AspNetCore.Components.EventCallback.Factory.Create<System.String>(Item, Microsoft.AspNetCore.Components.EventCallback.Factory.CreateInferred(Item, _value => typeof(TItem).GetProperty(PropertyName).SetValue(Item, _value), (string)typeof(TItem).GetProperty(PropertyName).GetValue(Item)));
        ValueExpression = Expression.Lambda<Func<string>>(Expression.Property(Expression.Constant(Item, typeof(TItem)), PropertyName));
    }
}

Using the above component you can then do

<CustomInputText TItem="TItem" Item="item" PropertyName="@col.PropertyName">

Make sure to wrap in in <EditForm>

Upvotes: 2

Just the benno
Just the benno

Reputation: 2601

In Blazor, a two-way binding consists of two steps.

  1. In the render process, the value will be read and displayed.
  2. It will subscribe to the change event of the component. The handler will write the value back to your property.

To let the compiler do the magic, you use @bind-Value instead of Value. However, you could "hack" the system by setting the event handler by yourself.

<InputText 
   Value="@typeof(TItem).GetProperty(col.PropertyName).GetValue(item)" 
   ValueChanged="@(e => typeof(TItem).GetProperty(col.PropertyName).SetValue(item,e))">
</InputText>

As a reference, have a look at https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-5.0.

Upvotes: 1

MestreDosMagros
MestreDosMagros

Reputation: 1030

You can get a value from a property on any object based on the name of the property itself.

Example

public class Program
{
    public static void Main()
    {
        var tst = new MyClass { MyProperty = 1 };           
        var propValue = tst.GetType().GetProperties().FirstOrDefault(p => p.Name == "MyProperty").GetValue(tst);
        Console.WriteLine(propValue);           
    }           
}

public class MyClass {
    public int MyProperty { get;set; }
}

Now you can just adapt this solution to your case

<Virtualize Items="ColumnDefinitions" Context="col">
    <td>                            
        <InputText @bind-Value="@typeof(TItem).GetProperties().FirstOrDefault(p => p.Name == col.PropertyName).GetValue(item);"></InputText>
    </td>
</Virtualize>

Hope this works, i dont work as much with razor files and cannot test this case right now, but at leats it can lead you to a solution

Upvotes: 1

Related Questions