user10483669
user10483669

Reputation: 588

Dynamically binding input-text to class/object propertys using Blazor

Im trying to build a dynamic list of input field for properties inside a class using Blazor but cant figur out how to bind/link the content of a input box to a property of a class. (the class have can have a large number of public props, not only Name and Description as in the below example, they are not always of the type "string")

lets say that I have this class/model:

public class customer{
        public string Name { get; set; }
        public int Age { get; set; }
        public string Description { get; set; }

}

I got this blazor component (updateC.razor):

@inherits CLogic    
@if (nfo != null)
{
      @foreach (var obj in nfo)
      {
             <input type="text" class="form-control"      
             [email protected]().GetProperty(obj.ToString())/>
      }
}

and finally Clogic:

public class Clogic: ComponentBase{
    [Parameter]
    public Customer SCustomer { get; set; } = new Customer();
    [Parameter]
    public PropertyInfo[] nfo { get; set; }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            nfo = SCustomer.GetType().GetProperties();
            StateHasChanged();
        }
    }
}

This is suppose to bind changes made in each input field to the correct property in the current instance of SCustomer (when input is made it is suppose to update the correct property of the class/object). This is not working, values inside of SCustomer are not changed after input are done. I'm guessing that i'm going about this completely wrong but can't seem to figure out how to make this work and can't find any examples doing this.

Upvotes: 9

Views: 7321

Answers (4)

Fred
Fred

Reputation: 12856

I solved this using generics. This lets each input type pass on a generic type which defines what type it wants to use.

<input @bind="Name.Value" />
<input @bind="Age.Value" />

<div><code>Name</code>: @Name.Value</div>
<div><code>Age</code>: @Age.Value</div>

@code {
    private FormField<string> Name { get; set; } = new FormField<string>();
    private FormField<int> Age { get; set; } = new FormField<int>();

    public class Form
    {
        public ICollection<IFormField> Fields { get; set; }
    }

    public interface IFormField
    {
        public int ControlType { get; set; }
    }

    public class FormField<T> : IFormField
    {
        public int ControlType { get; set; }
        public T Value { get; set; }
    }
}

Here it is on BlazorFiddle: https://blazorfiddle.com/s/wen1g26q

Upvotes: 0

Biju Kalanjoor
Biju Kalanjoor

Reputation: 552

@foreach(propertyInfo in nfo)
{
  <label>@propertyInfo.Name</label>
  <input type="text" value="@propertyInfo.GetValue(SCustomer)" @onchange="@((ChangeEventArgs __e) => 
    propertyInfo.SetValue(SCustomer,Convert.ChangeType(__e.Value, 
    propertyInfo.PropertyType,null))"/>
}

Upvotes: 1

user10483669
user10483669

Reputation: 588

I finally found a way of doing this, here is my solution: I created a helper class:

public class PropHolder
    {
        [Parameter]
        public PropertyInfo info { get; set; }
        [Parameter]
        public string type { get; set; }
        public PropHolder(PropertyInfo nfo, string propType)
        {
            info = nfo;
            type = propType;
        }
    }

then i created a dictionary of this class and some cheking functions (this is inside of Clogic)

[Parameter]
        public Dictionary<int, PropHolder> Props{ get; set; }
        public void GetAllProps()
        {
            Props = new Dictionary<int, PropHolder>();
            //nfo = SCustomer.GetType().GetProperties();
            int Cid = 0;
            foreach (PropertyInfo pif in SCustomer.GetType().GetProperties())
            {
                Props[Cid] = new PropHolder(pif, pif.PropertyType.Name);
                Cid++;
            }
        }

        public string CheckName(PropHolder propertyInfo)
        {
            if (propertyInfo.GetType() == typeof(PropHolder))
            {
                return propertyInfo.type;
            }
            else
            {
                return propertyInfo.GetType().Name.ToString();
            }

        }
        public PropertyInfo getInfo(PropHolder propertyInfo)
        {
            if (propertyInfo.GetType() == typeof(PropHolder))
            {
                return propertyInfo.info;
            }
            else
            {
                return null;
            }
        }

and finally im able to loop over the keys of my dictionary and bind all values correctly (got lots of help figuring this out from the answere given by: "agua from mars") here is the updateC.razor content:

@if (Props != null)
        {

            @foreach (int key in Props.Keys)
            {

                var pinfo = Props[key];
                @if (CheckName(pinfo) == "String")
                {
                    <input type="text" class="form-control" value=@(getInfo(pinfo).GetValue(SCustomer)) @onchange="@((ChangeEventArgs __e) => getInfo(pinfo).SetValue(SCustomer, __e.Value.ToString()))" />
                }
            }
        }

this gives me a input box for every prop that is of the type String, if additional types are to be handled it can now easily be added. This is probably not the best sulution but it do work. I will update the answere if any better working solutions are posted.

Upvotes: 0

agua from mars
agua from mars

Reputation: 17454

@foreach (var propertyInfo in nfo)
{
    <input type="text" class="form-control"      
    value="@propertyInfo.GetValue(SCustomer)" 
    @onchange="@((ChangeEventArgs __e) =>
       propertyInfo.SetValue(SCustomer, __e.Value.ToString()))" />
}

Upvotes: 8

Related Questions