In ASP.NET Core 3.0.0-preview3, we are removing the ability to access the Microsoft.Interop.IJSRuntime
via the static JSRuntime.Current
property. Application users may continue to access the IJSRuntime
instance by injecting it in to their components. For example,
Previously:
```C#
// Stock.cshtml
@functions {
public override void OnInit()
{
StocksService.OnStockTickerUpdated += stockUpdate =>
{
JSRuntime.Current.InvokeAsync<object>(
"handleTickerChanged", stockUpdate.symbol, stockUpdate.price);
};
}
}
After 3.0.0-preview3,
```C#
// Stock.cshtml
@inject IJSRuntime JSRuntime
@functions {
public override void OnInit()
{
StocksService.OnStockTickerUpdated += stockUpdate =>
{
JSRuntime.InvokeAsync<object>(
"handleTickerChanged", stockUpdate.symbol, stockUpdate.price);
};
}
}
Related issue: https://github.com/aspnet/AspNetCore/issues/6828
Is the Microsoft.Interop.JSRuntime
work with Blazor
only ? Or it can also can work with regular Mvc Page ?
What is the suggested way to inject JSRuntime into a non component class. (meaning just a stock .cs class) What would the replacement of the following become?
((IJSInProcessRuntime)JSRuntime.Current).Invoke
You'd have to pass it in. We have an example of it the templates:
```C#
// Before
public class ExampleJsInterop
{
public static Task
{
// Implemented in exampleJsInterop.js
return JSRuntime.Current.InvokeAsync
"exampleJsFunctions.showPrompt",
message);
}
}
```C#
// After
public static Task<string> Prompt(IJSRuntime jsRuntime, string message)
{
// Implemented in exampleJsInterop.js
return jsRuntime.InvokeAsync<string>(
"exampleJsFunctions.showPrompt",
message);
}
The framework does not (as yet) inject instances of IJSInPrcocessRuntime
, but downcasting it like you did here should work as before.
Thank you for the quick response
Another approach for non component class that needs JSRuntime would be still make the class a component so we can prevent passing down the JSRuntime to every method/class we need it.
Here is an Example:
Make a class which inherits from ComponentBase
Create an empty view like this an inherit the class
Then on the main layout component call the component for initialization
Now we can call the static methods with the JSRuntime already initialized :)
I like this approach. Thanks for this!
With Razor Components (server-side), each instance of IJSRuntime
is associated with a unique SignalR client. Using a static would be problematic.
Understood. We are using Blazor client side. I have implemented your parameter solution and it works well. Thanks again.
We were using the static method JSRuntime.Current.InvokeAsync in Blazor Hosted project to initialize js event listeners. These calls were made in the Client side Program.cs Main method. This was done to ensure the .NET runtime had been loaded. What would be the recommended way for this now?
For example:
public class Program
{
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
//Call any js event listener setup. Do this here because need to make sure the .NET side is loaded in the browser.
JSRuntime.Current.InvokeAsync<bool>("initScreenFull");
}
public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) =>
BlazorWebAssemblyHost.CreateDefaultBuilder()
.UseBlazorStartup<Startup>();
}
//JS FUNCTION CONCEPT
window.initScreenFull = () => {
if (typeof screenfull === 'undefined') return;
var $doc = $(document);
var $fsToggler = $('[data-toggle-fullscreen]');
// Not supported under IE
var ua = window.navigator.userAgent;
if (ua.indexOf("MSIE ") > 0 || !!ua.match(/Trident.*rv\:11\./)) {
$fsToggler.addClass('hide');
}
if (!$fsToggler.is(':visible')) // hidden on mobiles or IE
return;
$fsToggler.on('click', function (e) {
e.preventDefault();
if (screenfull.enabled) {
screenfull.toggle();
// Switch icon indicator
toggleFSIcon($fsToggler);
} else {
console.log('Fullscreen not enabled');
}
});
if (screenfull.raw && screenfull.raw.fullscreenchange)
$doc.on(screenfull.raw.fullscreenchange, function () {
toggleFSIcon($fsToggler);
});
function toggleFSIcon($element) {
if (screenfull.isFullscreen)
$element.children('em').removeClass('fa-expand').addClass('fa-compress');
else
$element.children('em').removeClass('fa-compress').addClass('fa-expand');
}
}
})(window, document, window.jQuery);
Why not move this JSInvoke to your Index.cshtml?
Good call @laurence73! Thanks so much! That seemed to work.
There should be an (easy) way to get to the IJsRuntime in a class library outside of a Component.
Would it be so bad to offer a IJSRuntimeProvider with a singleton scope that we could ask for the IJSRuntime on-demand? Or, something like IJSRuntimeProvider.ExecuteInScope(
We create primarily singleton scoped objects so that we don't make mistakes with state, and only being able to inject the IJSRuntime has forced these objects to be elevated to Scoped scope in order to be "correct".
Just hit an issue where I wanted to use JSRuntime in a static extension method (on NavigationManager to open a new window). Bit tricky now!
Most helpful comment
There should be an (easy) way to get to the IJsRuntime in a class library outside of a Component.