Пользовательский раскрывающийся список Blazor с выбором HTML и сортировкой по тексту, а не по значению для перечислений

Я пытаюсь создать собственный раскрывающийся компонент Blazor для перечислений, который может сортировать элементы перечисления по их отображаемому значению/тексту. У меня есть следующий код:

<div>
    <select value = "@this.SelectedValue" @onchange = "@this.OnSelectedValueChanged" @key = "@this.Items">
        @foreach (var value in this.Items)
        {
            // Use enum value as value and display name as text.
            if (value is Enum enumName)
            {
                <option id = "@value" value = "@value">@enumName.GetDisplayName(CultureInfo.CurrentCulture)</option>
            }
        }
    </select>
</div>

перечисление порядка сортировки:

public enum SortOrder
{
    None,
    Ascending,
    Descending
}

и в коде файла:

private List<TItem?> OriginalItems { get; set; } = [];

[Parameter]
[EditorRequired]
public IEnumerable<TItem?> Items { get; set; } = [];

[Parameter]
public TItem? SelectedValue { get; set; }

[Parameter]
public EventCallback<TItem?> SelectedValueChanged { get; set; }

[Parameter]
public EventCallback<TItem?> OnValueChanged { get; set; }

[Parameter]
public SortOrder SortOrder { get; set; }

protected override void OnInitialized()
{
    base.OnInitialized();
    this.OriginalItems = this.Items.ToList();
    this.SortItems();
}

protected async Task OnSelectedValueChanged(ChangeEventArgs args)
{
    if (args.Value is not string value)
    {
        return;
    }

    if (this.SelectedValue is Enum _)
    {
        try
        {
            var result = (TItem?)Enum.Parse(typeof(TItem?), value);
            await this.SelectedValueChanged.InvokeAsync(result);
            await this.OnValueChanged.InvokeAsync(result);
        }
        catch
        {
            // ignored
        }
    }
}

private void SortItems()
{
    if (this.SortOrder == SortOrder.None)
    {
        return;
    }

    if (typeof(TItem).BaseType == typeof(Enum))
    {
        this.Items = this.SortOrder == SortOrder.Ascending ?
            [.. this.Items.OrderBy(f => (f as Enum)?.GetDisplayNameForEnum(CultureInfo.CurrentCulture))] :
            [.. this.Items.OrderByDescending(f => (f as Enum)?.GetDisplayNameForEnum(CultureInfo.CurrentCulture))];
    }
}

Функция GetDisplayValue просто получает отображаемое значение для перечислений и реализуется для всех перечислений, помещенных в раскрывающийся список, например:

private static string GetDisplayName(this SortOrder order, CultureInfo culture)
{
    var de = culture.Name.StartsWith("de");

    return order switch
    {
        SortOrder.None => de ? "Keine" : "None",
        SortOrder.Ascending => de ? "Aufsteigend" : "Ascending",
        SortOrder.Descending => de ? "Absteigend" : "Descending",
        _ => $"[[{type}]]"
    };
}

Существует также поле ввода для фильтрации, метод фильтрации, размер фильтра и несколько других вещей, таких как установленные идентификаторы. Однако я удалил их, поскольку они не имеют отношения к вопросу. TItem является общим членом (и в моем случае используется только для Enums).

Когда я добавляю компонент на страницу, первоначальная сортировка работает. Поиск работает. Однако, когда я выбираю один из элементов, сортировка игнорируется и select, кажется, откатывается назад/"Сортируется" по значению члена перечисления (например, тому, что указано в value= из option). Есть ли способ всегда сортировать select по отображаемому имени даже после изменения выбора значения (после вызова OnSelectedValueChanged())?

Подсказка: вызов SortItems() в конце OnSelectedValueChanged() не работает. Подсказка 2: вызов SortItems() перед this.OriginalItems = this.Items.ToList(); в OnInitialized() тоже не помогает. Подсказка 3: использование индекса списка this.Items в качестве значения при выборе после сортировки списка также не работает, плюс имеет понижение версии, что выбранное значение установлено правильно, но больше не отображается в раскрывающемся списке.

Я в замешательстве и, вероятно, совершенно не понимаю сути. Если вы хотите создать выборку для перечисления, то почему вы передаете public IEnumerable<TItem?> Items { get; set; } = []? Конечно, вы просто передаете тип перечисления и выполняете итерацию Enum.GetValues в элементе управления, чтобы получить возможные значения?

MrC aka Shaun Curtis 21.03.2024 17:30

@MrCakaShaunCurtis Теоретически да, но в некоторых случаях я хотел исключить некоторых членов перечисления, а в некоторых — нет. Поэтому на родительской странице я могу решить, следует ли использовать все элементы или нет. Ограничение типа TItem: Enum было бы полезно, но почему-то не работает с Blazor и nullables.

FranzHuber23 21.03.2024 17:33
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Решение, которое я придумал сейчас, заключалось в том, чтобы переписать функцию SortItems, чтобы она возвращала значение, была ли коллекция изменена (например, изменена), а затем использовать OnAfterRender() для вызова StateHasChanged().

Изменения:

protected override void OnAfterRender(bool firstRender)
{
    if (!firstRender)
    {
        if (this.SortItems())
        {
            this.StateHasChanged();
        }
    }

    base.OnAfterRender(firstRender);
}

private bool SortItems()
{
    if (this.SortOrder == SortOrder.None)
    {
        return false;
    }

    if (typeof(TItem).BaseType == typeof(Enum))
    {
        var initialItems = this.Items.ToList();
        this.Items = this.SortOrder == SortOrder.Ascending ?
            [.. this.Items.OrderBy(f => (f as Enum)?.GetDisplayName(CultureInfo.CurrentCulture))] :
            [.. this.Items.OrderByDescending(f => (f as Enum)?.GetDisplayName(CultureInfo.CurrentCulture))];
        return !Enumerable.SequenceEqual(initialItems, this.Items);
    }

    return false;
}

Другие вопросы по теме