maui icon indicating copy to clipboard operation
maui copied to clipboard

Can't use JS interop outside the context of a component in MAUI

Open SteveSandersonMS opened this issue 3 years ago • 16 comments
trafficstars

Continuation of https://github.com/dotnet/maui/issues/1245 and https://github.com/dotnet/maui/issues/5726

We should look into adding functionality to help with this.

SteveSandersonMS avatar Apr 04 '22 16:04 SteveSandersonMS

@SteveSandersonMS do you have a suggestion for a workaround until this is fixed?

My problem:

In a WebView.Wpf Blazor app, read from IndexedDB using https://github.com/amuste/DnetIndexedDb before SetParametersAsync() is called anywhere.

In a WASM Blazor app this can be easily done after Build() and before RunAsync() (I did so in my old project):

        builder.Services.AddServices();
        WebAssemblyHost host = builder.Build();
        await host.Services.UseServices();
        await host.RunAsync();

Trying to do so in a WebView.Wpf Blazor app results in https://github.com/dotnet/maui/issues/1245

Right now, I am reading from IndexedDB in SetParametersAsync() of the MainLayout.razor.cs using my service that is registered as serviceCollection.AddScoped<IRepository, Repository>();

But SetParametersAsync() is called twice and the objects used in razor with @bind- are not the same objects that end up in my Repository - they don't have the same references.

If I use a bool flag to read from IndexedDB only once, then the razor components are left empty.

If I read from IndexedDB in OnInitializedAsync() or OnParametersSetAsync(), then the razor components are left empty.

This is my project, using .NET 6: https://github.com/Jinjinov/Ididit

This is my old project, using .NET 5, where I read from IndexedDB before RunAsync() https://github.com/Jinjinov/TheLastTime

Jinjinov avatar May 08 '22 20:05 Jinjinov

i have same issue with maui and i have something other from this! i solved this problem by some hacks but when i Call JSRuntime.InvokeAsync() my MAUI Application is freezed and it's Hanged and need to restart project! i cant solved second problem!

sajjadarashhh avatar Jun 21 '22 16:06 sajjadarashhh

@SteveSandersonMS as your question here we Use BlazoredLocalStorage in StartUp Project to fill values from localstorage, so we have some challenges with this issue in Blazor hybrid application .net maui! I used IJSInProcessRuntime to execute JS methods in property getter and setters by reflection and also it's working nice in WASM but when we migrate this to MAUI it has implementing exception(MAUI doesn't have this feature). I don't have problem with this so I use IJSRuntime instead of IJSInProcessRuntime in MAUI applications. Often it doesn't work in my StateContext ClassLibrary, and throws nullrefrence exception(before Main.razor OnInitializedAsync event) and sometimes my application is frozen until stopping my debugging! (after OnInitializedAsync) I think this issue has just solved when I inject IJSRuntime from Main.razor(and another razor components initialized). After that, I inject it to my StateContext ClassLibrary, sometimes it works and maybe it doesn't work becuase of freezing and NullRefrence errors. I think it can be solved by adding .AddWebViewService() method to initialize instances from webView in .net maui. such as : IJSRuntime IJSInProcessRuntime @mkArtakMSFT

sajjadarashhh avatar Jun 22 '22 09:06 sajjadarashhh

@sajjadarashhh Thanks for contacting us. For us to be able to investigate or take any action, we'd need a minimal sample that reproduces the issue. If you're able to produce a minimal, public GitHub repo that reproduces the problem, could you please file a new issue with a description?

SteveSandersonMS avatar Jun 22 '22 11:06 SteveSandersonMS

yeah but it's can be take while... wait I reproduced there

sajjadarashhh avatar Jun 22 '22 11:06 sajjadarashhh

@SteveSandersonMS I have reproduced test environment for this issues and take minimal hack for this problem in maui. we need IJSInProcessRuntime in MAUI. and another things to help we get services without exception errors...! https://github.com/sajjadarashhh/Dotnet.TestMauiFreeze

sajjadarashhh avatar Jun 23 '22 08:06 sajjadarashhh

@sajjadarashhh Thanks for supplying this repo. Unfortunately this repro isn't minimal - it contains a lot of application-specific code, and we don't have capacity to debug nontrivial application code for you. If you can reduce this to a minimal example (meaning the smallest possible number of lines of code added to the default project that will reproduce the problem), we'd be happy to work out whether there's a framework problem.

SteveSandersonMS avatar Jun 27 '22 09:06 SteveSandersonMS

yeah that's true I removed huge unused codes and make project minimal re-check and say me result @SteveSandersonMS

sajjadarashhh avatar Jun 28 '22 06:06 sajjadarashhh

@sajjadarashhh Sorry, but by "minimal" I'm referring to the minimal number of changes from the default project template to reproduce the issue. I think there's still quite a long way to go, and would recommend starting from a new project and adding the smallest number of changes to reproduce it, bearing in mind that we're not able to debug this for you (you'll need to show clearly what minimal sequence of calls has a problem, so that we can say whether or not this is a framework problem). Hope that's OK!

SteveSandersonMS avatar Jun 28 '22 09:06 SteveSandersonMS

@SteveSandersonMS I replace project and make 2 project for this problems:

  1. Object Reference null Exception
  2. Freeze Exception

sajjadarashhh avatar Jun 28 '22 10:06 sajjadarashhh

and both problems is not framework problem but BLAZOR HYBRID need these features to re-use WASM projects and we cant use best practices without this features:

  1. IJSInProcessRuntime
  2. call IJSRuntime.InvokeAsync() out of context.

sajjadarashhh avatar Jun 28 '22 10:06 sajjadarashhh

@sajjadarashhh Thanks for producing the smaller example. That really helps. It shows that, in the freezing case, it comes down to the following line:

JsRuntime.InvokeVoidAsync("window.location.reload").ConfigureAwait(false).GetAwaiter().GetResult();

Doing this is very much not supported. By blocking the main application thread with GetResult, it becomes impossible to receive the notification that the call has completed, so it will hang. I expect you're aware of this since you mention above the desire to have IJSInProcessRuntime. However, I'm not clear on how that could be possible given that WebView2 doesn't have synchronous call APIs. I have to recommend structuring your interop calls to be asynchronous since this is what the technology supports.

call IJSRuntime.InvokeAsync() out of context.

That is something we may be able to add, and is exactly what this issue tracks.

SteveSandersonMS avatar Jun 28 '22 11:06 SteveSandersonMS

Thanks for describe this. so we can't use completely BLAZOR WASM project in BLAZOR HYBRID and we should be make some change's such as you describe in above @SteveSandersonMS

sajjadarashhh avatar Jun 28 '22 12:06 sajjadarashhh

We've moved this issue to the Future milestone. This means that it is not going to be worked on for the coming release. We will reassess the issue following the current release and consider this item at that time.

ghost avatar Jul 13 '22 16:07 ghost

If this issue isn't going to be fixed anytime soon, how about implementing a better failure mode? Right now if a request is made outside of a component for an IJSRuntime the Maui injection service provides one that produces a null value failure when the program tries to use it. It took me most of the day before I worked my way to this site to understand what went wrong.

A better option would be to have it fail with a descriptive exception at some point, either when the dependency injection is attempted or when the IJSRuntime is used. Maybe an "IJSRuntimeInvalidOutsideComponent" exception. Put a nice description of the exception in the class description on the web and guys like me will be working on the workaround in less than an hour instead of sometime the next day.

Just a suggestion - I'm already past the point where that would have helped but the next guy might appreciate it. :)

jlinstrum avatar Sep 24 '22 04:09 jlinstrum

Any help for this case? I got stuck at this <BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html"> </BlazorWebView>

private async void button_Clicked(object sender, EventArgs e) { var js = blazorWebView.Handler.MauiContext.Services.GetService<IJSRuntime>(); var result = await js.InvokeAsync<string>("executeMe", "DEF"); Debug.WriteLine(result); } And the error was thrown when I tried to excute. See picture bellow image

vosonha89 avatar Dec 26 '22 10:12 vosonha89

Is there any update regarding this issue? I'm experiencing the same in a MAUI Blazor Hybrid App.

agermano avatar Jan 04 '23 18:01 agermano

Any updates on this issue or workarounds?

williamrdg avatar Jan 20 '23 03:01 williamrdg

@williamrdg For those in need of using IndexedDB and BlazoredLocalStorage. You can always create custom implementations of the desired interfaces. You can replace BlazoredLocalStorage implementation with that of Preferences and IndexedDB with SQLLite.

Then Inject them in MauiProgram.cs that way the service can be reused with both WASM and MAUI. Something like this. Not an ideal solution around but it gets the job done.

Serge-N avatar Feb 05 '23 07:02 Serge-N

@SteveSandersonMS I'm stealing this issue from you.

Eilon avatar Feb 14 '23 18:02 Eilon

Re-opening this one because the MAUI part isn't done yet.

Eilon avatar Feb 23 '23 23:02 Eilon

https://github.com/dotnet/maui/pull/13425 is merged and will be avilable in the next preview of .NET MAUI 8.

Eilon avatar Mar 31 '23 21:03 Eilon