diff --git a/README.md b/README.md index 2acb62d..bc77a2b 100644 --- a/README.md +++ b/README.md @@ -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] + + + +``` + +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: @@ -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: diff --git a/src/Piral.Blazor.Core/App.razor b/src/Piral.Blazor.Core/App.razor index 951ca12..93bba22 100644 --- a/src/Piral.Blazor.Core/App.razor +++ b/src/Piral.Blazor.Core/App.razor @@ -1,6 +1,7 @@ @inject IComponentActivationService Activation @implements IDisposable + @code { diff --git a/src/Piral.Blazor.Core/ComponentActivationService.cs b/src/Piral.Blazor.Core/ComponentActivationService.cs index c7e7631..d892871 100644 --- a/src/Piral.Blazor.Core/ComponentActivationService.cs +++ b/src/Piral.Blazor.Core/ComponentActivationService.cs @@ -11,7 +11,7 @@ namespace Piral.Blazor.Core; public class ComponentActivationService : IComponentActivationService { - private static readonly string appRoot = "approot"; + private static readonly string appRoot = "#approot"; private readonly Dictionary _components = new(); @@ -19,23 +19,30 @@ public class ComponentActivationService : IComponentActivationService private readonly List _active = new(); + private readonly List _provider = new(); + private readonly ILogger _logger; private readonly IModuleContainerService _container; - + private readonly NavigationManager _navigationManager; public event EventHandler ComponentsChanged; + public event EventHandler ProvidersChanged; + public event EventHandler RootChanged; public IEnumerable Components => _active; + public IEnumerable Providers => _provider; + public Type Root => GetComponent(appRoot) ?? typeof(DefaultRoot); private static readonly IReadOnlyCollection AttributeTypes = new List { typeof(PiralAppRootAttribute), + typeof(PiralProviderAttribute), typeof(PiralComponentAttribute), typeof(PiralExtensionAttribute), typeof(RouteAttribute), @@ -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); @@ -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 @@ -303,6 +324,8 @@ private static IEnumerable GetComponentNameToRegister(Type member, Type $"{((PiralComponentAttribute)attribute).Name ?? member.FullName}", Type _ when attributeType == typeof(PiralAppRootAttribute) => appRoot, + Type _ when attributeType == typeof(PiralProviderAttribute) => + $"provider-{member.FullName}", _ => null }); } diff --git a/src/Piral.Blazor.Core/ComponentView.razor b/src/Piral.Blazor.Core/ComponentView.razor index 23cf1d6..db808fa 100644 --- a/src/Piral.Blazor.Core/ComponentView.razor +++ b/src/Piral.Blazor.Core/ComponentView.razor @@ -3,7 +3,7 @@ @foreach (var active in Activation.Components) { -
+