dart-lang / web

Lightweight browser API bindings built around JS static interop.
https://pub.dev/packages/web
BSD 3-Clause "New" or "Revised" License
134 stars 23 forks source link

Package:web usage for migration #229

Open ebala96 opened 5 months ago

ebala96 commented 5 months ago

I'm doing a migration to 'package:web' and 'dart:js_interop'. I'm currently using the context object from the 'dart:js' package to store functions in the window object reference. However, I've noticed that there's no direct way to access the context from the 'package:web'.

I need help with this migration. Below is the code snippet I need to migrate

void _connectJsToFlutter({VoidCallback? then}) {
    js.context['$jsToDartConnectorFN$iframeViewType'] = (js.JsObject window) {
      jsWindowObject = window;
      for (final cb in widget.dartCallBacks) {
        jsWindowObject[cb.name] = cb.callBack;
      }

      jsWindowObject[webOnClickInsideIframeCallback] = (onClickCallbackObject) {
        _handleOnIframeClick(onClickCallbackObject as String);
      };

      webViewXController.connector = jsWindowObject;

      then?.call();
    };
}

Can you provide the correct usage of context and JsObject needed for the migration?

srujzs commented 5 months ago

globalContext is the dart:js_interop equivalent with JSObject being the equivalent to JsObject. Also note that we use static interfaces instead of class wrappers now with dart:js_interop.

A simple migration might look like the following:

import 'dart:js_interop';
import 'dart:js_interop_unsafe'; // to dynamically set properties using []=
...
void _connectJsToFlutter({VoidCallback? then}) {
    globalContext['$jsToDartConnectorFN$iframeViewType'] = (JSObject window) {
      JSObject jsWindowObject = window;
      for (final cb in widget.dartCallBacks) {
        jsWindowObject[cb.name] = cb.callBack.toJS;
      }

      jsWindowObject.setProperty(webOnClickInsideIframeCallback.toJS, (String onClickCallbackObject) {
        _handleOnIframeClick(onClickCallbackObject);
      }.toJS);

      webViewXController.connector = jsWindowObject;

      then?.call();
    };
}

It's a little difficult to tell what the types of some of these objects are, but a few key points here are:

For more information, look at https://dart.dev/interop/js-interop/usage and https://dart.dev/interop/js-interop/js-types

ebala96 commented 5 months ago

Thank you. Understood the usage now.

Also could you let me know if I am doing the right way of doing onbeforeunload.

window.onbeforeunload = () async{
      //my code snippet
}.toJS;

Or can I use a event listener to perform the same.

window.addEventListener('beforeunload', (JSAny data) {
      //my code snippet
    }.toJS);
srujzs commented 5 months ago

Yup, both should work (although that first one should take in the event as a parameter and I'm also not sure the async is necessary). You can also use EventStreamProvider in lib/src/helpers/events/streams.dart if you'd rather work with Streams: EventStreamProvider<BeforeUnloadEvent>('beforeunload').forTarget(window).

ebala96 commented 5 months ago

Also I am doing callMethod from JsObject and passing List<dynamic> params as the second parameter. JsObject if from'dart:js' package

For migration I am using JSObject from dart:js_interop_unsafe and doing a callMethodVarArgs call and passing the List params. How can I pass the List as [List<JSAny?>? arguments]?

Also, am I using the correct method from JSObject?

srujzs commented 5 months ago

It's better practice to define an interface for the JSObject using an extension type than use dart:js_interop_unsafe (see https://dart.dev/interop/js-interop/usage#interop-type-members on how to write an interop extension type), but if you don't statically know the method name, then dart:js_interop_unsafe and callMethodVarArgs is ok as the replacement.

How can I pass the List as [List<JSAny?>? arguments]?

What does the List contain? You'll need to convert the contents of the List to JSAnys. If it's a List<String> for example, you can do a list.map((e) => e.toDart).toList() to get a List<JSString>. Or if you have a literal e.g. ['hello', 'world'], you can just do ['hello'.toJS, 'world'.toJS] instead.

ebala96 commented 5 months ago

List contains dynamic type values. It contains string, boolean, number and Set/List.

srujzs commented 5 months ago

Assuming the nested Sets/Lists all contain types that can be converted as well (primitives or other nested structures), one thing you could try is using jsify which will convert all the entries in that list recursively.

List<dynamic> list = ...;
List<JSAny?> jsList = list.map((e) => jsify(e)).toList();
ebala96 commented 5 months ago

Where can I get more information or examples of JS interop usage other than https://dart.dev/interop/js-interop/usage?

srujzs commented 5 months ago

Besides the js-interop page and its subsections, I'd look at some examples of existing migrations like those in https://github.com/flutter/packages (just grep for dart:js_interop) or https://github.com/flutter/devtools. We have some tests in the Dart SDK as well that might be useful: https://github.com/dart-lang/sdk/tree/main/tests/lib/js/static_interop_test.

Of course, if you feel like you still have some questions, feel free to ask. If you want to provide feedback on what documentation might be useful for you or others, you can do so in https://github.com/dart-lang/site-www. There's some ongoing work there to get a "getting started" tutorial.