title: How to override InputBase<T> Value in Blazor, in order to validate
body: <p>I am trying to build a new Blazor input component, which inherits from InputBase, so that I can provide a form input field for selecting employees in an organisation. I need to provide the ability to limit the input to a single person or allow multiple selections, dependant on use case.</p>
  <p>The input component I am building accepts a List of type &quot;Employee&quot;, which is a class built specifically for our employee dataset. I have setup a Boolean value to denote if multi-selection should be allowed. However, I can't intercept the change in the Value to stop additional people being added if it should be limited to a single selection.</p>
  <p>I have tried the following override of the InputBase Value property:</p>
  <hr />
  <pre><code>    new public List&lt;Employee&gt; Value
      {
          get
          {
              return CurrentValue;
          }
          set
          {
              if (!Multiselect)
              {
                  CurrentValue = value.Take(1).ToList();
              }
              else
              {
                  CurrentValue = value;
              }
          }
      }
  </code></pre>
  <hr />
  <p>Due to having a List binding I have overridden the FormatValueAsString and TryParseValueFromString so that the email addresses can be seen in an input field, as part of the component. I can add multi-selection logic into these functions, however they still allow the Value to end up with multiple in, though the string shown in the UI will only contain one.</p>
  <p><strong>EDIT:</strong>
  Adding more code as requested.</p>
  <p>.razor file:</p>
  <pre><code>@inherits InputBase&lt;List&lt;Employee&gt;&gt;
  @using Project.Models.Employee
  
  &lt;div class=&quot;InputEmployee&quot;&gt;
      &lt;input class=&quot;InputEmployee&quot; @bind=&quot;CurrentValueAsString&quot; type=&quot;text&quot; /&gt;
      &lt;i class=&quot;oi oi-person&quot; @onclick=&quot;() =&gt; openSearch()&quot;&gt;&lt;/i&gt;
  &lt;/div&gt;
  &lt;div class=&quot;InputEmployeeSearch @(displaySearch ? &quot;&quot;: &quot; collapse&quot;)&quot; @onblur=&quot;() =&gt; toggleSearch()&quot; id=&quot;searchInput&quot;&gt;
      &lt;div class=&quot;row&quot;&gt;
          &lt;div class=&quot;searchInput&quot;&gt;
              &lt;input @bind-Value=&quot;searchString&quot; @bind-Value:event=&quot;oninput&quot; placeholder=&quot;Search by name, email address, employee number&quot; type=&quot;text&quot; /&gt;
          &lt;/div&gt;
      &lt;/div&gt;
      @if (activeSearch)
      {
          &lt;div class=&quot;row&quot;&gt;
              &lt;div class=&quot;searchActive&quot;&gt;
                  Searching...
              &lt;/div&gt;
          &lt;/div&gt;
      }
      @if (foundEmployees != null &amp;&amp; foundEmployees.Any())
      {
          &lt;div class=&quot;row&quot;&gt;
              &lt;div class=&quot;searchResults&quot;&gt;
                  @foreach (Employee employee in foundEmployees)
                  {
                      &lt;div class=&quot;employeeResult&quot; @onclick=&quot;() =&gt; select(employee)&quot;&gt;
                          @employee.DisplayName
                      &lt;/div&gt;
                  }
              &lt;/div&gt;
          &lt;/div&gt;
      }
      @if (errorMessage != null)
      {
          &lt;div class=&quot;row&quot;&gt;
              &lt;div class=&quot;searchError&quot;&gt;
                  @errorMessage
              &lt;/div&gt;
          &lt;/div&gt;
      }
  &lt;/div&gt;
  </code></pre>
  <p>.razor.cs file:</p>
  <pre><code>using Project.Models.Employee;
  using Project.Services.Employee;
  using Microsoft.AspNetCore.Components;
  using Microsoft.AspNetCore.Components.Forms;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;
  using System.Timers;
  
  namespace Project.Components
  {
      public partial class InputEmployee : InputBase&lt;List&lt;Employee&gt;&gt;
      {
          [Inject]
          private IEmployeeDataService EmployeeDataService { get; set; }
  
          [Parameter]
          public int Debounce { get; set; } = 500;
          [Parameter]
          public int MaximumResults { get; set; } = 10;
          [Parameter]
          public int MinimumLength { get; set; } = 4;
          [Parameter]
          public bool Multiselect { get; set; } = false;
          new public List&lt;Employee&gt; Value
          {
              get
              {
                  return CurrentValue;
              }
              set
              {
                  if (!Multiselect)
                  {
                      CurrentValue = value.Take(1).ToList();
                  }
                  else
                  {
                      CurrentValue = value;
                  }
              }
          }
  
          private string currentValueOverlay = &quot;&quot;;
          private bool activeSearch = false;
          private bool displaySearch = false;
          private Timer debounceTimer;
          private string searchString
          {
              get
              {
                  return searchText;
              }
  
              set
              {
                  searchText = value;
  
                  if (value.Length == 0)
                  {
                      debounceTimer.Stop();
                      activeSearch = false;
                  }
                  else
                  {
                      debounceTimer.Stop();
                      debounceTimer.Start();
                      activeSearch = true;
                  }
              }
          }
          private string searchText = &quot;&quot;;
          private string errorMessage;
          private List&lt;Employee&gt; foundEmployees;
  
          protected override void OnParametersSet()
          {
              base.OnParametersSet();
              debounceTimer = new Timer();
              debounceTimer.Interval = Debounce;
              debounceTimer.AutoReset = false;
              debounceTimer.Elapsed += search;
          }
  
          protected override bool TryParseValueFromString(string value, out List&lt;Employee&gt; result, out string validationErrorMessage)
          {
              result = new List&lt;Employee&gt;();
              validationErrorMessage = null;
  
              string[] valueArray = value.Split(&quot;;&quot;);
              List&lt;Employee&gt; output = new List&lt;Employee&gt;();
  
              foreach (string employeeEmail in valueArray)
              {
                  try
                  {
                      Employee employee = Task.Run(async () =&gt; await EmployeeDataService.FindEmployeesAsync(employeeEmail.Trim())).Result.FirstOrDefault();
  
                      if (employee != null)
                      {
                          output.Add(employee);
                      }
                  }
                  catch
                  {
                      validationErrorMessage = $&quot;User \&quot;{employeeEmail}\&quot; was not found.&quot;;
                  }
              }
  
              result = output;
              return true;
          }
  
          protected override string FormatValueAsString(List&lt;Employee&gt; employees)
          {
              string employeeString = &quot;&quot;;
  
              if (employees.Any() &amp;&amp; employees.FirstOrDefault() != null)
              {
                  employeeString = String.Join(&quot;; &quot;, employees.Select(x =&gt; x.Email).ToArray());
              }
  
              return employeeString;
          }
  
          private async void toggleSearch()
          {
              displaySearch = !displaySearch;
  
              await InvokeAsync(StateHasChanged);
          }
  
          private void openSearch()
          {
              foundEmployees = new List&lt;Employee&gt;();
  
              if (searchString.Length &gt;= MinimumLength)
              {
                  search(null, null);
              }
  
              toggleSearch();
          }
  
  #nullable enable
          private async void search(Object? source, ElapsedEventArgs? e)
          {
              errorMessage = null;
              foundEmployees = null;
              await InvokeAsync(StateHasChanged);
  
              if (int.TryParse(searchString, out int i))
              {
                  if (searchString.Length == 9)
                  {
                      List&lt;string&gt; searchList = new List&lt;string&gt;();
                      searchList.Add(searchString);
                      foundEmployees = (await EmployeeDataService.GetEmployeesAsync(searchList)).ToList();
                  }
                  else
                  {
                      updateError($&quot;Searching by employee number requires the full number.&quot;);
                  }
              }
              else
              {
                  if (searchString.Length &lt; MinimumLength)
                  {
                      updateError($&quot;You must enter at least {MinimumLength} characters of their name or email address.&quot;);
                  }
                  else
                  {
                      foundEmployees = (await EmployeeDataService.FindEmployeesAsync(searchString)).Take(MaximumResults).ToList();
                  }
              }
  
              if (foundEmployees != null &amp;&amp; !foundEmployees.Any())
              {
                  updateError($&quot;No employees found matching \&quot;{searchString}\&quot;.&quot;);
              }
  
              activeSearch = false;
              await InvokeAsync(StateHasChanged);
          }
  
          private async void updateError(string message)
          {
              activeSearch = false;
              errorMessage = message;
              await InvokeAsync(StateHasChanged);
          }
  
          private void select(Employee employee)
          {
              Value.Add(employee);
          }
      }
  }
  </code></pre>