Skip to content
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

Getting started with JS interop tutorial #6317

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions src/content/interop/js-interop/start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
---
title: Getting started with Javascript interop
description: A basic example of using browswer APIs and a bundled JS libarary.
---

In this tutorial, you'll learn the basics of interacting with JavaScript
in Dart, using various JavaScript and browser APIs.

```dart
import 'dart:js_interop';

// All top-level JS interop APIs need the @JS annotation.
@JS()
external JSObject get document;
```

We use an `@JS`-annotated external top-level getter to access the document that’s
available within `globalThis`. The result is a JS object, and is typed in Dart as a `JSObject`.
An opaque JS object is not very useful, so we define an interop type using
[extension types][] to view it differently:

```dart
@JS()
external Document get document;

extension type Document._(JSObject _) implements JSObject {}
```

We define an interface for the `JSObject`, which allows us to interact with the
`JSObject` by declaring more interop APIs:

```dart
extension type Document._(JSObject _) implements JSObject {
external JSObject createElement(JSString tag);
}
```

We’ve declared an external method within `Document` that allows us to call
instance methods on it, like:

```dart
var button = document.createElement('button'.toJS);
```

An important thing to note is that all values that flow into and out of interop
APIs should be typed as an interop type or an allowed Dart primitive type.
In order to convert some Dart values to a JS value and vice versa,
we use conversion methods like .toJS above.
In the case where a Dart primitive type is used,
the compiler automatically converts the Dart value to a JS value and vice versa,
so we can rewrite the above code as:

```dart
external JSObject createElement(String tag);
```

and call it as:

```dart
var button = document.createElement('button');
```

The above call creates a button element,
which we can add to the document body using `appendChild`:

```dart
extension type Document._(JSObject _) implements JSObject {
external JSObject createElement(String tag);
external Body get body;
}

extension type Body._(JSObject _) implements JSObject {
external JSObject appendChild(JSObject child);
}
```

```dart
var button = document.createElement('button');
document.body.appendChild(button);
```

It is useful sometimes to register event listeners
so that we know when the button is clicked:

```dart
extension type ButtonElement(JSObject _) implements JSObject {
external void addEventListener(String event, JSFunction listener);
}
```

```dart
var button = ButtonElement(document.createElement('button'));
document.body.appendChild(button);
button.addEventListener('click', (JSObject event) {
print('Clicked!');
}.toJS);
```

Here, we create an interface for a button element and register an event listener
for the “click” event using a Dart function that is converted to a JS function using toJS.
Functions converted using toJS have the same limitations as interop APIs:
their parameter and return types can only be an interop type or a primitive type.

There are other common types you may come across when using JS interop,
like Promises. We can convert Promises to and from Dart Futures as well:

```dart
import 'dart:js_interop';

extension type Response._(JSObject _) implements JSObject {
external bool get ok;
}

@JS()
external Response fetch(String resource);

void main() async {
var response = await fetch('image.png').toDart;
print(response.ok);
}
```

The above example uses the fetch API.

You may also come across Arrays, which also can be converted to and from Dart Lists:

```dart
import 'dart:js_interop';

@JS('Array.of')
external JSArray<JSString> arrayOf(String a, String b);

void main() {
var array = arrayOf('hello', 'world');
var list = array.toDart;
for (var element in list) {
print(element.toDart);
}
}
```

The above example uses `Array.of` using renaming to create an Array from two string values,
converts the Array to a List of `JSStrings`,
iterates over the List, and converts and prints out the string values.

An important thing to note when dealing with generic JS types like `JSArray`
and `JSPromise` is that the generic must be a JS value.
For example, with Lists, you’ll need to convert the contents before converting the List e.g.

```dart
List<JSString> list = ['hello'.toJS, 'world'.toJS];
list.toJS;
```

or

```dart
List<String> list = ['hello', 'world'];
list.map((e) => e.toJS).toList().toJS;
```

## Learn more

* For more information on which types have conversions, checkout [Conversions][].
* For more information on how to write interop APIs, checkout [Usage][].
* To access common utility functions, checkout:
* The [`dart:js_interop`][] library, and
* The [`dart:js_interop_unsafe`][] library.
* [`package:web`][] exposes many of the browser APIs
(including those used in the above examples) through interop declarations.

[Conversions]: https://dart.dev/interop/js-interop/js-types#conversions
[Usage]: https://dart.dev/interop/js-interop/usage
[`dart:js_interop`]: https://api.dart.dev/main/dart-js_interop/dart-js_interop-library.html
[`dart:js_interop_unsafe`]: https://api.dart.dev/main/dart-js_interop_unsafe/dart-js_interop_unsafe-library.html
[`package:web`]: /interop/js-interop/package-web
5 changes: 5 additions & 0 deletions src/content/interop/js-interop/tutorials.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ description: Tutorials for common JavaScript interop use cases in Dart.

## Tutorials

### [Getting started with Javascript interop][]



### [How to mock JavaScript interop in Dart][]

This tutorial will walk through how you can use Dart classes to mock the
Expand All @@ -15,4 +19,5 @@ TODO: add a section on how to bundle a JS and Dart app for interop
TODO: maybe add a section on conversions
{% endcomment %}

[Getting started with Javascript interop]: /interop/js-interop/start
[How to mock JavaScript interop in Dart]: /interop/js-interop/mock
Loading