-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Design considerations for libraries based on JS Interop #13296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks @rynowak (although I hope my text does not "blow" 😎) |
Fixed lol. Sorry for the bad typo.
It sounds like you already figured out the solution - if you want to totally abstract away the fact that JS Interop is used here, you're going to to want to create your own exception type and throw that consistently. Consumers of your library will need to be aware that it can fail, just like anything that's network-based.
The JS Runtime will throw during prerendering. My intuition is that this is a bug in your caller's code - and thus you should just let this bubble up. Component authors need to be aware that they can do a limited set of things during prerendering. Prerendering is not for everyone and not for every scenario.
For cases where the circuit is disconnected - we currently throw an InvalidOperationException. This isn't great, because I don't really ever want to see code that handles invalid operation exception. There are two things that give me pause thinking about disconnected states:
We'll also throw One thing we might consider here is to track whether or not the connection is open internally to the JS Runtime, and have an If the JS side throws an exception we return that in a When JS code returns bad data, we throw a |
@rynowak thanks for the input. Been busy at the (paying) job so completely forgot about this. I will see how I can use you say to build up a stable service and components now that P9 is about to land. |
@rynowak thanks for opening up this discussion. I'll try to map out a scenario where IComponentContext made things a bit better. So, a simple example would be an IClientRegionService that is obviously in the DI container as a specific implementation - this service needs to read some information from the client browser (let's not worry about how valid the information is - for our purposes it works great) - for example it gets the current local time from the client, to work out an offset for displaying localised times without the need for a JS lib on the page. In order to do this successfully the service needs to use JSInterop and therefore needs to know when the client is connected. The only lifecycle event we have would require a Razor Component that implements OnAfterRenderAsync and notifies the service that we are connected. So, to implement a service that provides localized information about the client, we are dependent on the application having a component that will be present in all layouts, but who's only job is to control the lifecycle events of a DI service. It wasn't ideal with IComponentContext either as there was still nothing we could await but at least it was possible to have an independent service that could detect for itself when it was safe to use JSInterop. I accept that it is possible to work this way, with a Component that does the job, but it would just be really nice to have that separation, so Services wouldn't have to rely on the lifecycle of a component. Of course the service could just keep trying JSInterop and handling errors, but I'd rather not do that if it can be avoided. A further thought is that I can easily imagine that it would be useful to know whether Blazor is connected in other parts of an application where Blazor forms only part of the surface, for instance if you have an MVC application that uses Blazor Components embedded in a view? I guess it would just be really useful to have a scoped service in the DI container that we can Inject and provides some kind of connection monitor. |
@SQL-MisterMagoo I think your scenario is somewhat similar to the one I posted in the original issue, adding it here for additional context: #12082 (comment) |
@egil Yeah, I guess so, I think my underlying feeling is that it's not 100% components that need to care about connected state. |
We are using a system that gets the clients timezone on blazor server side for use in components. At first it was done by a service that defaulted to a predefined timezone when IComponentContext was not connected. Now it has been moved over to a cascading parameter in the main layout (arguably a better API anyway), but still faces the same issue with pre-rendering where we have to use a default fallback value or handle null cases. Both methods add a lot of extra consideration with changing values or handling of cases that are only really there for initialisation. I would like to see a way to...
Thanks for letting me not write javascript! |
@Yen from preview 9, OnAfterRender* won't be called during prerendering anymore, so that should solve your main issue. There are however other reasons for JavaScript interop to fail, so you will probably still need some defensive code that does retrying for temporary issues. |
@egil I am not sure of the rational behind that decision, it seems to me that it just makes code more complex as the "OnAfterRender" function isnt being called after rendering, but that's a separate issue to this I think. For an example with my above solution, I had issues where I would assign a value derived from the fallback default timezone, then end up comparing it with another value derived from the now correctly updated cascading parameter. |
with HotSwap component the IComponentContext came critical for me as user can select some components on specific service so if there is no connection to backend service all those components dont have componentContext that time so no render and when connection is establish the rendered when those components Context is not null |
Can I ask for info about specific scenario details? Everyone here seems to be building services that are intended to be reusable. What do those services do? Having the real scenarios in mind will help us design solutions to this. |
@rynowak I need a service server side that can provide a js call to get the current browser timezone before prerender. Server side specifically. Thanks |
@rynowak: My service-scenario is this - PageVisibilityService.cs / pageVisibilityApiInterop.js.
This is really useful if you have any timers that updates the UI in your Blazor app, since the you can stop your timer and save ressources, when the users does not have the browser tab showing your app in the foreground. My implementation works like this:
This flow ensures that there is only one subscription per service-scope, i.e. browser tab, independent of how many components wants to track the browser tabs visibility. The components also do not need to know if the browser supports the Page Visibility API or if my service is able to establish a subscription to it, since my service will tell subscribers that the page is visible unless it explicitly knows otherwise. I dont know of the top of my head how many similar DOM APIs that exists in the browsers, but I think that quite a few of those would follow a similar pattern to that described here. Let me know if anything is unclear. Ps. the service is currently part of my effort to build a Bootstrap razor component library, but I will likely pull it out to an independent library in the future. |
Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue. This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue! |
The below text was posted by @egil on #12082 - moving it here so we can continue the discussion.
OK @rynowak, let me share my current thoughts, maybe you can fill in a few gaps :)
My scenario is primarily a component library/service library setting. Ideally, since Razor components leverages dependency injection, the users of my library doesn't need to know or should know whether or not the services or components in my library uses JSInterop. If they do, they would, like you say, have to be aware and handle of the various pitfalls with JSInterop.
If I later remove a components or services dependency on JSInterop (you guys might release new APIs later that means I don't need it), then all my users who have been good Razor citizens and added proper error handling for JSInterop, they would likely need to clean up their code again.
With the above in mind, I will as a library author always prefer to handle the expected JSInterop errors, and wrap the unexpected JSInterop errors in a custom exception type, that generally signal to users of my library that something unexpected happened. That way, the users of my libraries can have a more simplified error handling, or none at all, and at least one that is less likely to change.
Known or expected errors
This is where you can probably help -- what are the all the things that can go wrong in JSInterop world, and how to recognize each one?
There might be other considerations for client side blazor, and I cannot claim to have any experience with SingalR directly, so there might also be some protocol-level errors that can be handled gracefully, depending on scenario/context.
Thoughts?
The text was updated successfully, but these errors were encountered: