1

I was able to bind an int type (which is the first element of the list SelectedDiagnosisIdList) variable to the selected element of a html select:

<div>
    <label>Diagnosis:</label>
    <div>
        <select @bind="@userInfo.SelectedDiagnosisIdList[0]">
            @foreach (var item in diagnoses)
            {
                <option value="@item.Id">@item.Name</option>
            }
        </select>
        <p>@userInfo.SelectedDiagnosisIdList[0]"</p>
    </div>
</div>

@code
{
    List<int> SelectedDiagnosisIdList = new List<int>() { 0 };
    List<Diagnosis> diagnoses; //  populated from db in OnInitializedAsync

}

This nicely works, the value of the paragraph changes when I change the selected value on the UI.


Now, I want to add more Diagnoses, so I am trying to maintain more s and add more elements to the SelectedDiagnosisIdList:

<div>
    <label>Diagnosis:</label>
    <div>
        @for(int i = 0; i < userInfo.SelectedDiagnosisIdList.Count; i++)
            <select @bind="@userInfo.SelectedDiagnosisIdList[i]">
                @foreach (var item in diagnoses)
                {
                    <option value="@item.Id">@item.Name</option>
                }
            </select>
            <p>@userInfo.SelectedDiagnosisIdList[i]</p>
        }
    </div>
</div>

@code
{
    List<int> SelectedDiagnosisIdList = new List<int>() { 0 };
    List<Diagnosis> diagnoses; //  populated from db in OnInitializedAsync

}

This will spoil the program, nothing happens on the UI when I select an item and on the console:

blazor.webassembly.js:1 WASM: Unhandled exception rendering component:
blazor.webassembly.js:1 WASM: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
WASM: Parameter name: index
blazor.webassembly.js:1 WASM:   at (wrapper managed-to-native) System.Reflection.RuntimeMethodInfo.InternalInvoke(System.Reflection.RuntimeMethodInfo,object,object[],System.Exception&)
blazor.webassembly.js:1 WASM:   at System.Reflection.RuntimeMethodInfo.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) <0x20c1360 + 0x000ce> in <71c4fd446b1842ba93fd332e5f4f9c06>:0 
blazor.webassembly.js:1 WASM: --- End of stack trace from previous location where exception was thrown ---
blazor.webassembly.js:1 WASM:   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion (System.Threading.Tasks.Task task) <0x269de40 + 0x000e6> in <cbf249d7f04d4fa18d15bfae8ef7f389>:0 
WASM:   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask (System.Threading.Tasks.Task taskToHandle) <0x269e988 + 0x000c2> in <cbf249d7f04d4fa18d15bfae8ef7f389>:0 

I dont' understand what is the problem and how could I make it work... Please help

jps
  • 20,041
  • 15
  • 75
  • 79
Dániel Erős
  • 83
  • 1
  • 2
  • 8

1 Answers1

10

I guess your issue is related to for loop. You should define a local variable in your loop as follows:

    <div>
    @for(int i = 0; i < userInfo.SelectedDiagnosisIdList.Count; i++)
      {
        int local = i;
        <select id=$"dropdown{local+1}" 
               @bind="@userInfo.SelectedDiagnosisIdList[local]">
            @foreach (var item in diagnoses)
            {
                <option value="@item.Id">@item.Name</option>
            }
        </select>
        <p>@userInfo.SelectedDiagnosisIdList[local]</p>
    }
    </div>

Hope this helps...

enet
  • 41,195
  • 5
  • 76
  • 113
  • 1
    Amazing, it did the trick :) Can you explain why it worked? – Dániel Erős Feb 14 '20 at 21:26
  • 4
    When you use the for loop without a local variable, the runtime access the i variable which contains the value at the end of the loop. But when you use a local variable, at each iteration of the loop a new variable is created to store the new value, and thus when the runtime accesses the variables, each contain a different value. – enet Feb 14 '20 at 21:44
  • You can read more about this issue here: https://csharpindepth.com/Articles/Closures – enet Feb 14 '20 at 22:11
  • 1
    It's primarily due to the fact that the rendering is done in an Async manner, so the for loop is executed inside a lambda, the local variable introduces the variable into the lambda. There's one other thing you'll need to make the code work correctly too. HTML Id attributes on the actual select elements EG: id="dropdown1", id="dropdown2" and so on, so that the enclosing form knows which drop down is which when a selection is made. – shawty Jul 28 '20 at 20:35
  • Exactly, I had to use HTML Id's to get this to work. – Dániel Erős Oct 17 '20 at 09:35