Home > Software design >  refer to components that created by loop
refer to components that created by loop

Time:09-23

I want to create a components by following this steps:

  • I have a list of items.
  • I want to loop in this list and create a component like InputNumber.
  • Add EventCallback to the generic created InputNumber that accept ref of this Inputtext because I want to use this ref to set the focus on this InputNumber.
  • I have also onblure method that execute some code for me, and I am using the onfocus to return focus to the input after execute this code by onblure

My question
How can I get this ref and send it as parameter of EventCallback?
The problem here that this components have been generated by loop, so I don't want to create by hand hundred variables to represent ref's.
My concept code like this:

@code{
  private void OnFocus(MyInputNumber<double?> obj)
  {
     if (obj is not null)
     {
       obj!.Element.Value.FocusAsync();
     }
  }
}
@foreach(var MyItem in MyList)
{
  <EditForm Model="MyItem">
    //Some components ..
    <label>
        Test
        <InputNumber @bind-Value="MyItem.MyVal"
         @onfocus="@((InputNumber<double?> obj @*wrong*@) =>  
    OnFocus(obj))"
    @onblur=@(() => OnblureHandler(context))
    </label>
 </EditForm>
}

If you see up the parameter InputNumber<double?> obj, this way is wrong, usually I use @ref=SomeVariable but becasue I created in generic way, I can not do that.

Note:

I don't to adjust my list to be dictionary<MYItemType,InputNumber<double?>>, or create a new class that has InputNumber<double?> as property. I am searching for different way, like go from editcontext to any input has been modified and reset focus on it, I don't know if that possible !

CodePudding user response:

You can add an InputNumber<double?> InputNumberRef { get; set; } property to your model class. Then is the foreach loop bind it to the component reference @ref="MyItem.InputNumberRef" then you can pass it in your callback method @onblur="() => HandleBlur(MyItem.InputNumberRef)".

Here is the demo code that I used. The following code after input onblur event it waits 2 seconds and returns the focus to the input.

@page "/"

@foreach (var item in _items)
{
    <EditForm Model="@item">
        <InputNumber  @ref="@item.InputNumberRef" @bind-Value="@item.Value" @onblur="() => HandleBlur(item.InputNumberRef)" />
    </EditForm>
}

@code {

    private List<Item> _items = new List<Item>
    {
        new Item { Value = 10 },
        new Item { Value = 30 },
        new Item { Value = 20 },
    };

    private async Task HandleBlur(InputNumber<int> inputNumberRef)
    {
        if (inputNumberRef.Element.HasValue)
        {
            await Task.Delay(2000);
            await inputNumberRef.Element.Value.FocusAsync();
        }
    }

    public class Item
    {
        public int Value { get; set; }
        public InputNumber<int> InputNumberRef { get; set; }
    }
}

Credits to user @enet for suggesting this solution in a different question on stackoverflow.

CodePudding user response:

Not sure why you're looking for a different solution, when the best one was already provided by Dimitris Maragkos

You can create a custom component, deriving from InputNumber and set its focus internally in the OnAfterRenderAsync method :

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    base.OnInitialized();
    await this.Element.Value.FocusAsync();
}
  • Related