diff --git a/src/componentsBase/IIgniteUIBlazorRuntime.cs b/src/componentsBase/IIgniteUIBlazorRuntime.cs
index fb991f99..f6d57cab 100644
--- a/src/componentsBase/IIgniteUIBlazorRuntime.cs
+++ b/src/componentsBase/IIgniteUIBlazorRuntime.cs
@@ -1,9 +1,10 @@
+using System.Runtime.CompilerServices;
using Microsoft.JSInterop;
namespace IgniteUI.Blazor.Controls
{
///
- /// The runtime behind the marker; the components and
+ /// The runtime behind ; the components and
/// reach it through .
///
internal interface IIgniteUIBlazorRuntime
@@ -19,14 +20,29 @@ internal interface IIgniteUIBlazorRuntime
internal static class IgniteUIBlazorRuntimeExtensions
{
+ private static readonly ConditionalWeakTable _adopted = new();
+
///
- /// The single conversion from the public marker to the runtime it is implemented by.
+ /// The single conversion from the public service to the runtime it is implemented by.
///
internal static IIgniteUIBlazorRuntime AsRuntime(this IIgniteUIBlazor service)
{
- return service as IIgniteUIBlazorRuntime
- ?? throw new InvalidOperationException(
- $"The registered {nameof(IIgniteUIBlazor)} instance is of type {service?.GetType().Name ?? "null"}, which is not the runtime registered by AddIgniteUIBlazor.");
+ if (service is IIgniteUIBlazorRuntime runtime)
+ {
+ return runtime;
+ }
+ if (service != null && IsFullPackageRuntime(service.GetType()))
+ {
+ return _adopted.GetValue(service, s => new IgniteUIBlazor(s.JsRuntime));
+ }
+ throw new InvalidOperationException(
+ $"The registered {nameof(IIgniteUIBlazor)} instance is of type {service?.GetType().Name ?? "null"}, which is not the runtime registered by AddIgniteUIBlazor.");
}
+
+ // Temporary, while the full IgniteUI.Blazor package deprecates its IgniteUIBlazor class: an app that registers
+ // that class instead of calling AddIgniteUIBlazor gets a runtime of its own, one per instance and so one per scope.
+ // Matched by name because that package depends on this one.
+ private static bool IsFullPackageRuntime(Type type) =>
+ type.FullName == "IgniteUI.Blazor.Controls.IgniteUIBlazor" && type.Assembly.GetName().Name == "IgniteUI.Blazor";
}
}
diff --git a/src/componentsBase/IgbComponentBase.cs b/src/componentsBase/IgbComponentBase.cs
index 87d3caa4..f51a04f7 100644
--- a/src/componentsBase/IgbComponentBase.cs
+++ b/src/componentsBase/IgbComponentBase.cs
@@ -3857,6 +3857,12 @@ internal IgniteUIBlazorSettings(IIgniteUIBlazorSettings settings)
///
public interface IIgniteUIBlazor
{
+ ///
+ /// The JS runtime the client resources load through. Supports the product infrastructure and is not
+ /// intended to be used directly from your code.
+ ///
+ [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
+ IJSRuntime JsRuntime { get; }
}
internal class IgniteUIBlazor : IIgniteUIBlazor, IIgniteUIBlazorRuntime
diff --git a/tests/IgniteUI.Blazor.Tests/ServiceRegistrationTests.cs b/tests/IgniteUI.Blazor.Tests/ServiceRegistrationTests.cs
index d24c32b7..e9cf564d 100644
--- a/tests/IgniteUI.Blazor.Tests/ServiceRegistrationTests.cs
+++ b/tests/IgniteUI.Blazor.Tests/ServiceRegistrationTests.cs
@@ -1,3 +1,6 @@
+using System.Reflection;
+using System.Reflection.Emit;
+using Bunit;
using IgniteUI.Blazor.Controls;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
@@ -180,6 +183,54 @@ public void AddIgniteUIBlazor_MutatingOriginalArray_DoesNotAffectRegisteredModul
Assert.Equal([typeof(FirstModule)], SettingsOf(host).ModulesToLoad);
}
+ [Fact]
+ public void Runtime_ExposesTheRegisteredJsRuntime()
+ {
+ using var host = new Host(s => s.AddIgniteUIBlazor());
+
+ Assert.Same(host.Services.GetRequiredService(), host.Services.GetRequiredService().JsRuntime);
+ }
+
+ /// The full IgniteUI.Blazor package's deprecated IgniteUIBlazor, registered in place of AddIgniteUIBlazor.
+ [Fact]
+ public void Component_WithFullPackageRuntimeRegistered_RequestsItsModuleThroughItsJsRuntime()
+ {
+ using var context = new BunitContext();
+ context.JSInterop.Mode = JSRuntimeMode.Loose;
+ context.Services.AddScoped(sp =>
+ {
+ var runtime = (HandRegisteredRuntime)Activator.CreateInstance(_fullPackageRuntimeType)!;
+ runtime.JsRuntime = sp.GetRequiredService();
+ return runtime;
+ });
+
+ context.Render();
+
+ var load = Assert.Single(context.JSInterop.Invocations, v => v.Identifier == "igRequestLoad");
+ Assert.Equal("WebButtonModule", load.Arguments[0]);
+ }
+
+ [Fact]
+ public void Component_WithOtherRuntimeRegistered_Throws()
+ {
+ using var context = new BunitContext();
+ context.Services.AddScoped(_ => new HandRegisteredRuntime());
+
+ var error = Assert.Throws(() => context.Render());
+ Assert.Contains("not the runtime registered by AddIgniteUIBlazor", error.Message);
+ }
+
+ public class HandRegisteredRuntime : IIgniteUIBlazor
+ {
+ public IJSRuntime JsRuntime { get; set; } = null!;
+ }
+
+ // HandRegisteredRuntime under the full package's type and assembly name; this repository cannot reference that package.
+ private static readonly Type _fullPackageRuntimeType = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("IgniteUI.Blazor"), AssemblyBuilderAccess.Run)
+ .DefineDynamicModule("IgniteUI.Blazor")
+ .DefineType("IgniteUI.Blazor.Controls.IgniteUIBlazor", TypeAttributes.Public, typeof(HandRegisteredRuntime))
+ .CreateType();
+
[Fact]
public void Runtime_IsScoped_SoEachScopeRegistersItsOwn()
{