Skip to content

Commit

Permalink
Introduced v15 changes w.r.t. providers
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianRappl committed Apr 22, 2024
1 parent abe7eab commit 80f0dae
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 9 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,26 @@ To dynamically change / refresh your components when the language change you'll

This way, your components will always remain up-to-date and render the right translations.

## Provider Components

Sometimes Blazor components require some global components (or "providers") to be added. To accomplish this you can create components marked with the `PiralProviderAttribute` attribute.

Example:


```razor
@attribute [PiralProvider]
<MudThemeProvider/>
<MudDialogProvider/>
<MudSnackbarProvider/>
```

Providers will *never* receive any parameters - they are rendered only once and will remain active for the whole lifecycle of the application. There can be more than one provider.

Provider components are adjacent to your other components, which may come and go and will be - in general - somewhere else in the DOM. As such they are not ideal for providing some cascading value or other properties. They are ideal, however, when you need something running all the time.

In contrast, Piral also has the concept of a root component, which comes with another set of constraints.

### Root Component

By default, the Blazor pilets run in a dedicated Blazor application with no root component. If you need a root component, e.g., to provide some common values from a `CascadingValue` component such as `CascadingAuthenticationState` from the `Microsoft.AspNetCore.Components.Authorization` package, you can actually override the default root component:
Expand Down Expand Up @@ -582,7 +602,7 @@ You can also provide your own providers here (or nest them as you want):

**Note**: There is always just one `PiralAppRoot` component. If you did not supply one then the default `PiralAppRoot` will be used. If you already provided one, no other `PiralAppRoot` can be used.

It is critical to understand that each attached pilet component starts its own Blazor rendering tree. Therefore, while there is just a single `PiralAppRoot` component there might be multiple instances active at a given point in time.
It is critical to understand that each attached pilet component starts its own Blazor rendering tree. Therefore, while there is just a single `PiralAppRoot` component there might be multiple instances active at a given point in time. This is a crucial difference to `PiralProvider` components, which are essentially singletons from a rendering perspective.

## Running and Debugging the Pilet :rocket:

Expand Down
1 change: 1 addition & 0 deletions src/Piral.Blazor.Core/App.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@inject IComponentActivationService Activation
@implements IDisposable

<PiralProviders />
<RenderView Type="@Activation.Root" Parameters="@parameters" />

@code {
Expand Down
35 changes: 29 additions & 6 deletions src/Piral.Blazor.Core/ComponentActivationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,38 @@ namespace Piral.Blazor.Core;

public class ComponentActivationService : IComponentActivationService
{
private static readonly string appRoot = "approot";
private static readonly string appRoot = "#approot";

private readonly Dictionary<string, Type> _components = new();

private readonly Dictionary<string, PiralElement> _elements = new();

private readonly List<ActiveComponent> _active = new();

private readonly List<Type> _provider = new();

private readonly ILogger<ComponentActivationService> _logger;

private readonly IModuleContainerService _container;

private readonly NavigationManager _navigationManager;

public event EventHandler ComponentsChanged;

public event EventHandler ProvidersChanged;

public event EventHandler RootChanged;

public IEnumerable<ActiveComponent> Components => _active;

public IEnumerable<Type> Providers => _provider;

public Type Root => GetComponent(appRoot) ?? typeof(DefaultRoot);

private static readonly IReadOnlyCollection<Type> AttributeTypes = new List<Type>
{
typeof(PiralAppRootAttribute),
typeof(PiralProviderAttribute),
typeof(PiralComponentAttribute),
typeof(PiralExtensionAttribute),
typeof(RouteAttribute),
Expand All @@ -55,6 +62,11 @@ public void Register(string componentName, Type componentType)
{
_logger.LogWarning("The provided component name has already been registered.");
}
else if (componentName.StartsWith("provider-"))
{
_provider.Add(componentType);
ProvidersChanged?.Invoke(this, EventArgs.Empty);
}
else
{
_components.Add(componentName, componentType);
Expand All @@ -70,12 +82,21 @@ public void Unregister(string componentName)
{
if (_components.TryGetValue(componentName, out var componentType))
{
DeactivateComponent(componentName);
_components.Remove(componentName);

if (componentName == appRoot)
if (componentName.StartsWith("provider-"))
{
RootChanged?.Invoke(this, EventArgs.Empty);
_provider.Remove(componentType);
ProvidersChanged?.Invoke(this, EventArgs.Empty);
}
else
{
DeactivateComponent(componentName);
_components.Remove(componentName);

if (componentName == appRoot)
{
RootChanged?.Invoke(this, EventArgs.Empty);
}
}
}
else
Expand Down Expand Up @@ -303,6 +324,8 @@ private static IEnumerable<string> GetComponentNameToRegister(Type member, Type
$"{((PiralComponentAttribute)attribute).Name ?? member.FullName}",
Type _ when attributeType == typeof(PiralAppRootAttribute) =>
appRoot,
Type _ when attributeType == typeof(PiralProviderAttribute) =>
$"provider-{member.FullName}",
_ => null
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/Piral.Blazor.Core/ComponentView.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

@foreach (var active in Activation.Components)
{
<div id="@active.ReferenceId">
<div id="@active.ReferenceId" style="display: none">
<div style="display: contents">
<RenderView Type="@active.Component" Parameters="@active.Args" />
</div>
Expand Down
12 changes: 11 additions & 1 deletion src/Piral.Blazor.Core/IComponentActivationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ public interface IComponentActivationService
/// The handler to monitor when the active components changed.
/// </summary>
event EventHandler ComponentsChanged;

/// <summary>
/// The handler to monitor when the available providers changed.
/// </summary>
event EventHandler ProvidersChanged;

/// <summary>
/// The handler to monitor when the root component changed.
Expand All @@ -20,6 +25,11 @@ public interface IComponentActivationService
/// </summary>
IEnumerable<ActiveComponent> Components { get; }

/// <summary>
/// Gets the currently available providers.
/// </summary>
IEnumerable<Type> Providers { get; }

/// <summary>
/// Gets a mounted element using its reference ID.
/// </summary>
Expand All @@ -29,4 +39,4 @@ public interface IComponentActivationService
/// Gets the configured root component.
/// </summary>
Type Root { get; }
}
}
25 changes: 25 additions & 0 deletions src/Piral.Blazor.Core/PiralProviders.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@inject IComponentActivationService Activation
@implements IDisposable

@foreach (var provider in Activation.Providers)
{
<DynamicComponent Type="@provider" />
}

@code {
private void ProvidersChanged(object sender, EventArgs e)
{
this.StateHasChanged();
}

public void Dispose()
{
Activation.ProvidersChanged -= ProvidersChanged;
}

protected override Task OnInitializedAsync()
{
Activation.ProvidersChanged += ProvidersChanged;
return base.OnInitializedAsync();
}
}
12 changes: 12 additions & 0 deletions src/Piral.Blazor.Utils/PiralProviderAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Piral.Blazor.Utils;

[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class PiralProviderAttribute : Attribute
{
/// <summary>
/// Registers a Piral provider component.
/// </summary>
public PiralProviderAttribute() { }
}

0 comments on commit 80f0dae

Please sign in to comment.