Aspnetcore: [Discussion] Microsoft.Interop.JSRuntime.Current has been removed

Created on 2 Mar 2019  路  14Comments  路  Source: dotnet/aspnetcore

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

Most helpful comment

There should be an (easy) way to get to the IJsRuntime in a class library outside of a Component.

All 14 comments

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($"{_fullTypeName}.Clear");

You'd have to pass it in. We have an example of it the templates:

```C#
// Before
public class ExampleJsInterop
{
public static Task Prompt(string message)
{
// 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
image
Create an empty view like this an inherit the class
image
Then on the main layout component call the component for initialization
image
Now we can call the static methods with the JSRuntime already initialized :)
image

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?

  1. Add @inject IJSRuntime JSRuntime to your Index.cshtml file
  2. Call await JSRuntime.InvokeAsync("initScreenFull") in the OnInitAsync method

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( ) if you didn't want to offer the instance to singleton scoped execution. (ie - You wanted to prevent people from caching the reference)

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!

Was this page helpful?
0 / 5 - 0 ratings