[dev-v5] Migrate AutoComplete component
@dvoituron it would be really great if you can also bind List<T> or Array<T> to this component. In v4 your property needs to be of type IEnumerable<T>. If you declared your property as List<T> you cannot bind it to the component.
List<T> inherits the IEnumerable<T>. This is the base type.
@dvoituron yes I know. But you cannot bind to it in v4. Try it for yourself:
@inject DataSource Data
<FluentAutocomplete TOption="Country"
AutoComplete="off"
Autofocus="true"
Label="Select a country"
Width="250px"
MaxAutoHeight="@(AutoHeight ? "200px" : null)"
Placeholder="Select countries"
OnOptionsSearch="@OnSearchAsync"
OptionDisabled="@(e => e.Code == "au")"
MaximumSelectedOptions="5"
OptionText="@(item => item.Name)"
@bind-SelectedOptions="@SelectedItems" />
<p>
<b>Selected</b>: @(String.Join(" - ", SelectedItems.Select(i => i.Name)))
</p>
<p>
<FluentSwitch @bind-Value="@AutoHeight" Label="Auto Height" /><br />
When the <code>MaxAutoHeight</code> attribute is set, the component adapts its height in relation to the selected elements.
</p>
@code
{
bool AutoHeight = false;
List<Country> SelectedItems = [];
private async Task OnSearchAsync(OptionsSearchEventArgs<Country> e)
{
var allCountries = await Data.GetCountriesAsync();
e.Items = allCountries.Where(i => i.Name.StartsWith(e.Text, StringComparison.OrdinalIgnoreCase))
.OrderBy(i => i.Name);
}
}
Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<System.Collections.Generic.List<FluentUI.Demo.Shared.SampleData.Country>>' to 'Microsoft.AspNetCore.Components.EventCallback'
I don't think that's a good idea. IEnumerable is the base type. Why arbitrarily choose List<T> or Array<T>? The “problem” will be the same with all other collection objects. So it's always best to use the base type.
@dvoituron In my case I was using List<T> every time when I explicitly want to know within my object that this is a collection where stuff can be added.
For example I have some filters like this:
public sealed class BerichteFilter : PageFilterBase
{
public string Kurzbezeichnung { get; set; } = string.Empty;
public IEnumerable<BerichtStatus> Stati { get; set; } = [];
public int UserId { get; set; }
public bool NurEigeneAnzeigen { get; set; }
public List<string> SachbearbeiterNummern { get; } = [];
public IEnumerable<User> Anlageuser { get; set; } = [];
public int? BerichtId { get; set; }
public IEnumerable<Berichtart> Berichtarten { get; set; } = [];
public IEnumerable<AddressType> AddressTypes { get; set; } = [];
public DateTime? AnlageDatum { get; set; }
public IEnumerable<Messe> Messen { get; set; } = [];
}
As you can see I have 5 IEnumerable<T> here. This indicates that I use them within a FluentAutoComplete. In some cases I have other filters like this where the IEnumerable<T> is pre-defined and I only use the component to display the values. Now you have to remember which of this properties are actually List<T> or Array<T> and you have to cast them every time you want to modify it outside of the component itself.
Perhaps you know a way how you can still use IEnumerable<T> but still use all types of implementations here.
I would say that your code should also use interfaces rather than List<T>.
In summary (helped by Copilot 😊)...
✅ Why Prefer Abstractions?
1. Flexibility and Maintainability Using abstractions allows you to change the underlying implementation without affecting the consuming code. For example, you can switch from List<T> to LinkedList<T> or a custom collection without breaking the API contract. [c# - Why r...e type ...], [Guidelines...Guidelines]
2. Encapsulation and Clean API Design Concrete types expose many methods that may not be relevant or safe for consumers. For instance, List<T> includes BinarySearch, Sort, and RemoveAt, which might not be appropriate for all use cases. Abstractions help hide unnecessary details and enforce cleaner usage. [Guidelines...Guidelines]
3. Improved Testability Interfaces are easier to mock in unit tests. This leads to looser coupling and better separation of concerns, which is especially valuable in large-scale systems like those you work on. [Prefer Int...yteCrafted]
4. Better Performance in Some Cases Using well-known empty collection instances (e.g., via EmptyIfNull() from R9Docs) can reduce memory usage and improve performance by avoiding allocations. [R9Docs]
🚫 What to Avoid
- Avoid returning or accepting List<T> or Dictionary<TKey, TValue> in public APIs unless absolutely necessary. [Guidelines...Guidelines]
- Avoid using ArrayList, Hashtable, or other legacy collections in modern C# code. [Guidelines...Guidelines]
Or make a component with :
@typeparam TItem
@typeparam TCollection where TCollection : IEnumerable<TItem>
But then you have to provide an extra parameter instead of doing casts ;D