FodCustomMultipleGroupSelect
Documentație pentru componenta FodCustomMultipleGroupSelect
1. Descriere Generală
FodCustomMultipleGroupSelect<T>
este o componentă de selecție multiplă cu suport pentru grupare a opțiunilor. Permite utilizatorilor să selecteze mai multe elemente dintr-o listă organizată în grupuri, cu checkbox-uri pentru fiecare opțiune.
Caracteristici principale: - Selecție multiplă cu checkbox-uri - Grupare opțiuni cu etichete - Afișare opțiuni selectate inline - Suport pentru opțiuni dezactivate/ascunse - Dropdown cu scroll pentru liste lungi - Validare integrată cu EditForm - Tipizare generică cu constrângere ICustomSelectItem - Stilizare inclusă
2. Utilizare de Bază
Selecție multiplă simplă
<FodCustomMultipleGroupSelect T="CustomOption"
@bind-Value="selectedOptions"
Source="optionGroups"
DefaultOptionText="Selectați opțiunile..." />
@code {
private List<CustomOption> selectedOptions = new();
private IEnumerable<CustomSelectItemGroup> optionGroups;
protected override void OnInitialized()
{
optionGroups = new List<CustomSelectItemGroup>
{
new CustomSelectItemGroup
{
Label = "Opțiuni principale",
Members = new List<CustomOption>
{
new() { Value = "1", Label = "Opțiunea 1" },
new() { Value = "2", Label = "Opțiunea 2" }
}
}
};
}
public class CustomOption : ICustomSelectItem
{
public string Value { get; set; }
public string Label { get; set; }
public bool Disabled { get; set; }
public bool Hidden { get; set; }
public bool isChecked { get; set; }
}
}
Selecție cu grupuri multiple
<FodCustomMultipleGroupSelect T="PermissionItem"
@bind-Value="selectedPermissions"
Source="permissionGroups" />
@code {
private List<PermissionItem> selectedPermissions = new();
private IEnumerable<CustomSelectItemGroup> permissionGroups = new[]
{
new CustomSelectItemGroup
{
Label = "Citire",
Members = new List<PermissionItem>
{
new() { Value = "read_users", Label = "Citire utilizatori" },
new() { Value = "read_reports", Label = "Citire rapoarte" },
new() { Value = "read_settings", Label = "Citire setări" }
}
},
new CustomSelectItemGroup
{
Label = "Scriere",
Members = new List<PermissionItem>
{
new() { Value = "write_users", Label = "Modificare utilizatori" },
new() { Value = "write_reports", Label = "Creare rapoarte" },
new() { Value = "write_settings", Label = "Modificare setări" }
}
},
new CustomSelectItemGroup
{
Label = "Administrare",
Members = new List<PermissionItem>
{
new() { Value = "delete_users", Label = "Ștergere utilizatori", Disabled = true },
new() { Value = "admin_system", Label = "Administrare sistem" }
}
}
};
}
3. Parametri
Proprietate | Tip | Descriere | Valoare Implicită |
---|---|---|---|
Value |
List<T> |
Lista elementelor selectate | new List<T>() |
Source |
IEnumerable<CustomSelectItemGroup> |
Sursa de date cu grupuri | - |
DefaultOptionText |
string |
Text afișat când nu există selecție | "Select..." |
DefaultOption |
bool |
Afișează opțiunea implicită | true |
OnValueChanged |
EventCallback<List<T>> |
Eveniment la schimbare selecție | - |
Readonly |
bool |
Dezactivează componenta | false |
4. Interfețe și Modele
ICustomSelectItem
public interface ICustomSelectItem
{
string Value { get; set; } // Valoarea unică
string Label { get; set; } // Textul afișat
bool Disabled { get; set; } // Dezactivat
bool Hidden { get; set; } // Ascuns
bool isChecked { get; set; } // Stare selecție
}
CustomSelectItemGroup
public class CustomSelectItemGroup
{
public string Label { get; set; } // Numele grupului
public IEnumerable<ICustomSelectItem> Members { get; set; } // Membrii grupului
}
5. Exemple Avansate
Selector de roluri și permisiuni
<EditForm Model="userModel" OnValidSubmit="SaveUser">
<DataAnnotationsValidator />
<FodCustomMultipleGroupSelect T="RolePermission"
@bind-Value="userModel.Permissions"
Source="rolePermissionGroups"
DefaultOptionText="Selectați rolurile și permisiunile..."
OnValueChanged="UpdatePermissionSummary" />
@if (selectedCount > 0)
{
<FodText Typo="Typo.caption" Color="FodColor.Secondary">
@selectedCount permisiuni selectate
</FodText>
}
<ValidationMessage For="@(() => userModel.Permissions)" />
<FodButton Type="submit" Color="FodColor.Primary">
Salvează utilizator
</FodButton>
</EditForm>
@code {
private UserModel userModel = new();
private int selectedCount = 0;
private IEnumerable<CustomSelectItemGroup> rolePermissionGroups = new[]
{
new CustomSelectItemGroup
{
Label = "Roluri standard",
Members = new List<RolePermission>
{
new() {
Value = "role_viewer",
Label = "Vizualizator",
Description = "Poate doar vizualiza date"
},
new() {
Value = "role_editor",
Label = "Editor",
Description = "Poate edita date existente"
},
new() {
Value = "role_creator",
Label = "Creator",
Description = "Poate crea date noi"
}
}
},
new CustomSelectItemGroup
{
Label = "Permisiuni speciale",
Members = new List<RolePermission>
{
new() {
Value = "perm_export",
Label = "Export date",
RequiresApproval = true
},
new() {
Value = "perm_bulk_ops",
Label = "Operații în masă",
RequiresApproval = true
},
new() {
Value = "perm_api_access",
Label = "Acces API",
Disabled = !userModel.IsVerified
}
}
}
};
private void UpdatePermissionSummary(List<RolePermission> permissions)
{
selectedCount = permissions.Count;
// Verifică permisiuni care necesită aprobare
var requiresApproval = permissions.Any(p => p.RequiresApproval);
if (requiresApproval)
{
ShowNotification("Unele permisiuni necesită aprobare suplimentară",
FodSeverity.Warning);
}
}
public class RolePermission : ICustomSelectItem
{
public string Value { get; set; }
public string Label { get; set; }
public bool Disabled { get; set; }
public bool Hidden { get; set; }
public bool isChecked { get; set; }
// Proprietăți adiționale
public string Description { get; set; }
public bool RequiresApproval { get; set; }
}
}
Selector de categorii cu subcategorii
<FodCustomMultipleGroupSelect T="CategoryItem"
@bind-Value="selectedCategories"
Source="categoryGroups"
DefaultOptionText="Alegeți categoriile..." />
<div class="mt-3">
<h5>Categorii selectate:</h5>
@foreach (var category in selectedCategories)
{
<FodChip Color="FodColor.Primary" Size="FodSize.Small" Class="ma-1">
@category.Label
</FodChip>
}
</div>
@code {
private List<CategoryItem> selectedCategories = new();
private IEnumerable<CustomSelectItemGroup> categoryGroups;
protected override async Task OnInitializedAsync()
{
// Încarcă categoriile din API
var categories = await CategoryService.GetCategoriesAsync();
categoryGroups = categories
.Where(c => c.ParentId == null)
.Select(parent => new CustomSelectItemGroup
{
Label = parent.Name,
Members = categories
.Where(c => c.ParentId == parent.Id)
.Select(child => new CategoryItem
{
Value = child.Id.ToString(),
Label = child.Name,
ParentCategory = parent.Name,
Icon = child.Icon
})
});
}
public class CategoryItem : ICustomSelectItem
{
public string Value { get; set; }
public string Label { get; set; }
public bool Disabled { get; set; }
public bool Hidden { get; set; }
public bool isChecked { get; set; }
public string ParentCategory { get; set; }
public string Icon { get; set; }
}
}
Selector dinamic cu filtrare
<div class="mb-3">
<FodTextField @bind-Value="searchTerm"
Label="Caută opțiuni"
Immediate="true"
DebounceInterval="300"
OnDebounceIntervalElapsed="FilterOptions" />
</div>
<FodCustomMultipleGroupSelect T="DynamicOption"
@bind-Value="selectedOptions"
Source="filteredGroups"
DefaultOptionText="Selectați din rezultate..." />
@code {
private List<DynamicOption> selectedOptions = new();
private IEnumerable<CustomSelectItemGroup> allGroups;
private IEnumerable<CustomSelectItemGroup> filteredGroups;
private string searchTerm = "";
protected override void OnInitialized()
{
allGroups = GenerateAllGroups();
filteredGroups = allGroups;
}
private void FilterOptions()
{
if (string.IsNullOrWhiteSpace(searchTerm))
{
filteredGroups = allGroups;
return;
}
filteredGroups = allGroups
.Select(group => new CustomSelectItemGroup
{
Label = group.Label,
Members = group.Members
.Cast<DynamicOption>()
.Where(m => m.Label.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) ||
m.Tags.Any(t => t.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)))
})
.Where(g => g.Members.Any());
StateHasChanged();
}
public class DynamicOption : ICustomSelectItem
{
public string Value { get; set; }
public string Label { get; set; }
public bool Disabled { get; set; }
public bool Hidden { get; set; }
public bool isChecked { get; set; }
public List<string> Tags { get; set; } = new();
}
}
6. Stilizare Personalizată
Componenta include stiluri inline, dar pot fi suprascrse:
/* Override stiluri implicite */
.fod-custom-select .fod-dropdown {
max-height: 300px; /* Mărime dropdown */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* Grupuri cu culori diferite */
.fod-custom-select .fod-group:nth-child(odd) {
background-color: #f8f9fa;
}
/* Opțiuni selectate evidențiate */
.fod-custom-select .fod-option input[type="checkbox"]:checked + label {
font-weight: 600;
color: var(--fod-palette-primary-main);
}
/* Chips pentru elemente selectate */
.fod-custom-select .selected-item {
background-color: var(--fod-palette-primary-light);
color: var(--fod-palette-primary-contrastText);
font-size: 0.875rem;
}
/* Opțiuni dezactivate */
.fod-custom-select .fod-option[disabled] {
opacity: 0.5;
cursor: not-allowed;
background-color: #f5f5f5;
}
7. Integrare cu Validare
<EditForm Model="formModel">
<DataAnnotationsValidator />
<FodCustomMultipleGroupSelect T="SkillItem"
@bind-Value="formModel.RequiredSkills"
Source="skillGroups" />
<ValidationMessage For="@(() => formModel.RequiredSkills)" />
</EditForm>
@code {
public class FormModel
{
[Required(ErrorMessage = "Selectați cel puțin o competență")]
[MinLength(1, ErrorMessage = "Minim o competență necesară")]
[MaxLength(5, ErrorMessage = "Maxim 5 competențe")]
public List<SkillItem> RequiredSkills { get; set; } = new();
}
}
8. Evenimente și Callback-uri
<FodCustomMultipleGroupSelect T="NotificationOption"
@bind-Value="userNotifications"
Source="notificationGroups"
OnValueChanged="HandleNotificationChange" />
@code {
private async Task HandleNotificationChange(List<NotificationOption> selected)
{
// Verifică combinații incompatibile
if (selected.Any(n => n.Value == "email_all") &&
selected.Any(n => n.Value.StartsWith("email_specific")))
{
ShowWarning("Nu puteți selecta 'Toate email-urile' împreună cu opțiuni specifice");
}
// Salvează preferințe
await UserPreferencesService.SaveNotificationSettings(selected);
}
}
9. Best Practices
- Implementare ICustomSelectItem - Asigurați implementarea completă
- Grupare logică - Organizați opțiunile în grupuri clare
- Limite de selecție - Impuneți limite pentru UX mai bun
- Feedback vizual - Afișați numărul de selecții
- Performanță - Pentru liste mari, implementați virtualizare
- Accesibilitate - Includeți etichete descriptive
10. Performanță
Pentru liste mari de opțiuni:
@code {
private async Task<IEnumerable<CustomSelectItemGroup>> LoadOptionsAsync()
{
// Încarcă doar grupurile vizibile inițial
var visibleGroups = await OptionService.GetTopGroupsAsync(10);
// Încarcă restul asincron
_ = Task.Run(async () =>
{
var allGroups = await OptionService.GetAllGroupsAsync();
await InvokeAsync(() =>
{
optionGroups = allGroups;
StateHasChanged();
});
});
return visibleGroups;
}
}
11. Troubleshooting
Selecțiile nu se păstrează
- Verificați că
Value
este unic pentru fiecare opțiune - Verificați implementarea
isChecked
în model - Asigurați-vă că lista Value este inițializată
Grupurile nu apar
- Verificați că
Source
conține date - Verificați că
Members
nu este gol pentru fiecare grup - Verificați că modelul implementează
ICustomSelectItem
Dropdown nu se închide
- Adăugați handler pentru click în afara componentei
- Implementați
@onfocusout
pentru închidere automată
12. Concluzie
FodCustomMultipleGroupSelect
oferă o soluție completă pentru selecții multiple organizate în grupuri. Cu suport pentru validare, personalizare și o interfață intuitivă cu checkbox-uri, componenta este ideală pentru scenarii complexe de selecție în aplicațiile FOD.