Blazor WebAssembly+ReactiveUI MVVM:ViewModel调用JS函数依赖注入问题
Great question! The core issue here is that your ReactiveUI ViewModel isn't being managed by Blazor's dependency injection (DI) container. Blazor automatically handles injection for components that inherit from ComponentBase, but it won't inject dependencies into ViewModels you instantiate manually or that are created outside the DI pipeline. Let's walk through the fix step by step.
Step 1: Register Your ViewModel with Blazor's DI Container
First, you need to tell Blazor's DI system to manage your TestViewModel instance. Open your Program.cs and add this line (choose the appropriate lifetime: Scoped, Transient, or Singleton based on your needs):
builder.Services.AddScoped<TestViewModel>();
Step 2: Use Constructor Injection for IJSRuntime
Property injection with the [Inject] attribute only works for Blazor components (classes inheriting ComponentBase). For ViewModels, constructor injection is the reliable approach. Update your ViewModel to accept IJSRuntime via the constructor, and use ReactiveCommand.CreateFromTask since we'll be calling an asynchronous JavaScript method:
using ReactiveUI; using Microsoft.JSInterop; using System.Reactive; using System.Threading.Tasks; public class TestViewModel : ReactiveObject { private readonly IJSRuntime _jsRuntime; public ReactiveCommand<Unit, Unit> AlertCommand { get; } // Inject IJSRuntime via constructor public TestViewModel(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; // Use CreateFromTask for async command logic AlertCommand = ReactiveCommand.CreateFromTask(ShowAlertMessage); } private async Task ShowAlertMessage() { // Await the async JS invocation to avoid fire-and-forget issues await _jsRuntime.InvokeAsync<bool>("showAlert", "Hello World!"); } }
Step 3: Inject the ViewModel into Your Blazor Component
Instead of manually instantiating the ViewModel in your component, let Blazor's DI inject it. Here's how your component might look:
@page "/test-alert" @inject TestViewModel ViewModel <h3>Test Alert from ViewModel</h3> <button class="btn btn-primary" @onclick="ExecuteAlertCommand"> Show Alert </button> @code { private void ExecuteAlertCommand() { // Subscribe to the command execution (handle errors if needed) ViewModel.AlertCommand.Execute().Subscribe(); } }
Why Your Original Approach Failed
- Manual ViewModel Instantiation: If you were creating the ViewModel with
new TestViewModel(), Blazor's DI had no way to injectIJSRuntimeinto it. The DI container can only inject dependencies into objects it creates itself. - Property Injection Limitation: The
[Inject]attribute is processed by Blazor's component activation pipeline, which doesn't apply to regular classes like your ViewModel. Constructor injection is the standard way to get dependencies into non-component classes in Blazor.
Key Notes
- Always
awaitIJSRuntime.InvokeAsyncto ensure the JavaScript call completes properly and to handle any potential exceptions. - Use
ReactiveCommand.CreateFromTaskfor asynchronous command logic instead ofReactiveCommand.Create, which is meant for synchronous operations. - Choose the correct DI lifetime for your ViewModel:
Scopedis usually a good fit for Blazor components, as it matches the component's lifecycle.
内容的提问来源于stack exchange,提问作者Rahul

