FodPopoverService
Descriere Generală
FodPopoverService
este serviciul responsabil pentru gestionarea centralizată a componentelor popover în aplicație. Permite înregistrarea, actualizarea și dezînregistrarea popover-urilor, oferind un mecanism pentru afișarea conținutului flotant poziționat relativ la elementele părinte.
Configurare
Înregistrare în Dependency Injection
// În Program.cs sau Startup.cs
builder.Services.AddScoped<IFodPopoverService, FodPopoverService>();
// Cu opțiuni personalizate
builder.Services.Configure<PopoverOptions>(options =>
{
options.ContainerClass = "popover-container";
options.FlipMargin = 5;
});
builder.Services.AddScoped<IFodPopoverService, FodPopoverService>();
Configurare JavaScript
// În fișierul JS principal
window.fodPopover = {
initilize: function (containerClass, flipMargin) {
// Inițializare popover manager
this.containerClass = containerClass;
this.flipMargin = flipMargin;
this.popovers = new Map();
},
connect: function (id) {
// Conectare popover nou
const element = document.getElementById(id);
if (element) {
this.popovers.set(id, element);
// Setup positioning logic
}
},
disconnect: function (id) {
// Deconectare popover
this.popovers.delete(id);
},
dispose: function () {
// Cleanup
this.popovers.clear();
}
};
Interfața IFodPopoverService
public interface IFodPopoverService
{
FodPopoverHandler Register(RenderFragment fragment);
Task<bool> Unregister(FodPopoverHandler handler);
IEnumerable<FodPopoverHandler> Handlers { get; }
Task InitializeIfNeeded();
event EventHandler FragmentsChanged;
}
Clase asociate
FodPopoverHandler
Reprezintă un handler pentru un popover individual.
Proprietăți:
- Id: Guid
- Identificator unic
- Fragment: RenderFragment
- Conținutul de randat
- IsConnected: bool
- Indică dacă este conectat la DOM
- Class: string
- Clase CSS
- Style: string
- Stiluri inline
- Tag: object
- Tag personalizat
- ShowContent: bool
- Indică dacă conținutul este vizibil
- UserAttributes: Dictionary<string, object>
- Atribute utilizator
Metode:
- Initialize()
- Inițializează conexiunea cu JS
- Detach()
- Deconectează de la JS
- UpdateFragment()
- Actualizează conținutul
- SetComponentBaseParameters()
- Setează parametrii de bază
PopoverOptions
public class PopoverOptions
{
public string ContainerClass { get; set; } = "fod-popover-container";
public int FlipMargin { get; set; } = 5;
}
Metode disponibile
Register
Înregistrează un nou popover.
Parametri:
- fragment: RenderFragment
- Conținutul popover-ului
Returnează: FodPopoverHandler
- Handler pentru gestionarea popover-ului
Unregister
Dezînregistrează un popover existent.
Parametri:
- handler: FodPopoverHandler
- Handler-ul de dezînregistrat
Returnează: Task<bool>
- True dacă dezînregistrarea a reușit
InitializeIfNeeded
Inițializează serviciul dacă nu a fost deja inițializat.
Parametri: Niciun parametru
Returnează: Task
- Task pentru așteptarea inițializării
Evenimente
FragmentsChanged
Declanșat când lista de fragmente se modifică (adăugare/ștergere).
Exemple de utilizare
Utilizare de bază cu FodPopover
@inject IFodPopoverService PopoverService
<FodPopover>
<ActivatorContent>
<FodButton>Deschide Popover</FodButton>
</ActivatorContent>
<ChildContent>
<div class="popover-content">
<h5>Titlu Popover</h5>
<p>Conținut popover</p>
</div>
</ChildContent>
</FodPopover>
@code {
// FodPopover folosește intern PopoverService
}
Gestionare manuală popover
@inject IFodPopoverService PopoverService
@implements IDisposable
<div @ref="anchorElement" @onclick="TogglePopover">
Click pentru popover
</div>
@code {
private ElementReference anchorElement;
private FodPopoverHandler popoverHandler;
private bool isOpen = false;
private async Task TogglePopover()
{
if (!isOpen)
{
// Înregistrare popover
popoverHandler = PopoverService.Register(
@<div class="custom-popover">
<h6>Popover Manual</h6>
<p>Conținut gestionat manual</p>
<FodButton Size="FodSize.Small" @onclick="ClosePopover">
Închide
</FodButton>
</div>
);
await popoverHandler.Initialize();
isOpen = true;
}
else
{
await ClosePopover();
}
}
private async Task ClosePopover()
{
if (popoverHandler != null)
{
await PopoverService.Unregister(popoverHandler);
popoverHandler = null;
isOpen = false;
}
}
public void Dispose()
{
_ = ClosePopover();
}
}
Service pentru tooltip-uri globale
public class TooltipService
{
private readonly IFodPopoverService _popoverService;
private readonly Dictionary<string, FodPopoverHandler> _tooltips = new();
public TooltipService(IFodPopoverService popoverService)
{
_popoverService = popoverService;
}
public async Task<string> ShowTooltip(RenderFragment content, TooltipOptions options)
{
var tooltipId = Guid.NewGuid().ToString();
var handler = _popoverService.Register(builder =>
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", $"tooltip {options.Class}");
builder.AddAttribute(2, "style", options.Style);
builder.AddContent(3, content);
builder.CloseElement();
});
await handler.Initialize();
_tooltips[tooltipId] = handler;
return tooltipId;
}
public async Task HideTooltip(string tooltipId)
{
if (_tooltips.TryGetValue(tooltipId, out var handler))
{
await _popoverService.Unregister(handler);
_tooltips.Remove(tooltipId);
}
}
public async Task HideAllTooltips()
{
foreach (var handler in _tooltips.Values)
{
await _popoverService.Unregister(handler);
}
_tooltips.Clear();
}
}
Popover cu actualizare dinamică
@inject IFodPopoverService PopoverService
<div class="user-card" @onclick="ShowUserDetails">
<img src="@user.Avatar" />
<span>@user.Name</span>
</div>
@code {
[Parameter] public UserModel user { get; set; }
private FodPopoverHandler userPopover;
private UserDetails userDetails;
private bool isLoading = true;
private async Task ShowUserDetails()
{
// Înregistrare popover cu loading
userPopover = PopoverService.Register(BuildPopoverContent());
await userPopover.Initialize();
// Încărcare date asincron
userDetails = await LoadUserDetails(user.Id);
isLoading = false;
// Actualizare conținut
userPopover.UpdateFragment(BuildPopoverContent(), this, "user-details-popover", "", true);
}
private RenderFragment BuildPopoverContent() => builder =>
{
if (isLoading)
{
builder.OpenComponent<FodLoadingCircular>(0);
builder.CloseComponent();
}
else
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "user-details");
builder.OpenElement(2, "h5");
builder.AddContent(3, userDetails.FullName);
builder.CloseElement();
builder.OpenElement(4, "p");
builder.AddContent(5, userDetails.Email);
builder.CloseElement();
builder.OpenElement(6, "p");
builder.AddContent(7, userDetails.Department);
builder.CloseElement();
builder.CloseElement();
}
};
}
Integrare cu FodPopoverProvider
<!-- În MainLayout.razor sau App.razor -->
<FodPopoverProvider />
@code {
// FodPopoverProvider gestionează randarea tuturor popover-urilor înregistrate
}
Tratare erori
Service cu logging
public class LoggingPopoverService : IFodPopoverService
{
private readonly FodPopoverService _innerService;
private readonly ILogger<LoggingPopoverService> _logger;
public FodPopoverHandler Register(RenderFragment fragment)
{
_logger.LogDebug("Înregistrare popover nou");
var handler = _innerService.Register(fragment);
_logger.LogDebug("Popover înregistrat cu ID: {PopoverId}", handler.Id);
return handler;
}
public async Task<bool> Unregister(FodPopoverHandler handler)
{
_logger.LogDebug("Dezînregistrare popover: {PopoverId}", handler?.Id);
try
{
return await _innerService.Unregister(handler);
}
catch (Exception ex)
{
_logger.LogError(ex, "Eroare la dezînregistrarea popover: {PopoverId}", handler?.Id);
return false;
}
}
}
Note tehnice
- Thread-safe initialization - Folosește SemaphoreSlim pentru inițializare sigură
- Event-driven updates - Notifică schimbările prin evenimente
- JS interop - Necesită funcții JavaScript pentru positioning
- Memory management - Important să dezînregistrați popover-urile nefolosite
- Render fragments - Gestionează RenderFragment pentru conținut dinamic
Bune practici
- Lifecycle management - Dezînregistrați popover-urile în Dispose
- Avoid memory leaks - Nu păstrați referințe la handlers după unregister
- Error handling - Gestionați TaskCanceledException la dispose
- Performance - Limitați numărul de popover-uri active simultan
- Accessibility - Asigurați navigare cu tastatura pentru popover-uri
- Mobile support - Testați comportamentul pe dispozitive touch
Concluzie
FodPopoverService oferă o infrastructură robustă pentru gestionarea popover-urilor în aplicații Blazor. Cu suport pentru actualizări dinamice, gestionare centralizată și integrare JavaScript pentru positioning avansat, serviciul facilitează crearea de interfețe utilizator interactive și informative.