If we take into account the enormous amount of JavaScript code and libraries that exist, and are still being developed, it is very important that we have a simple way to use JavaScript code from within Dart applications, in particular to get access from Dart to the JavaScript code that is running in the same web page. The earliest attempts used window.postMessage
, and then a package called js
was built. Because of the huge importance of this topic, the Dart team now has provided us with a core library, dart:js
, to interoperate with JavaScript. This provides better performance, reduces the size of the compiled JavaScript file, and makes it also easier to use. Once dart:js
is ready, the package js
has been rewritten to use dart:js
under the covers.
Take a look at the project js_interop
, as explained in the following steps:
dart:js
in our project, we have to import it in our code:import 'dart:js';
js_interop.html
, we declare a Dart script, and the JavaScript program js_interop.dart
will look into the code of interact.js
:<script type="application/dart" src="js_interop.dart"></script> <script type="application/javascript" src="interact.js"></script>
interact.js
file contains the following code: a variable jsvar
, a class Person
with the properties name
and gender
, and the methods greeting
and sayHello
:var jsvar = "I want Dart"; function Person(name, gender) { this.name = name; this.gender = gender; this.greeting = function(otherPerson) { alert('I greet you ' + otherPerson.name); }; } Person.prototype.sayHello = function () { alert ('hello, I am ' + this.name ); };
var dart = context['jsvar'];
print(dart); // I want Dart
Person
object:var pers1 = new JsObject(context['Person'], ['An', 'female']); var pers2 = new JsObject(context['Person'], ['John', 'male']);
print(pers1['name']); // An print(pers2['gender']); // male pers2['gender'] = 'female'; print(pers2['gender']); // female
Person
object:pers1.callMethod('sayHello', []); pers2.callMethod('greeting', [pers1]);
The preceding steps display alert windows with the messages hello, I am An and I greet you An.
context
, and display an alert window with callMethod
: context.callMethod('alert', ['Hello from Dart!']);
jsify
to create a JavaScript object and array: var jsMap = new JsObject.jsify({'a': 1, 'b': 2});
print(jsMap); // [object Object]
var jsArray = new JsObject.jsify([1, 2, 3]);
print(jsArray); // [1, 2, 3]
The dart:js
library provides Dart access to JavaScript objects in web applications, not in server applications. More specifically, it exposes wrapped or proxy versions of any JavaScript objects you access. This enables Dart to safely sandbox JavaScript away and prevents its problems from leaking into the Dart application. You can get and set properties and call JavaScript functions and methods on JavaScript objects, while conversions between Dart and JavaScript are taken care of as far as possible. At this moment, the bridge is not fully bidirectional; JavaScript has no access to Dart objects, but it can call Dart functions.
The main type of object is JsObject
with which we can reach out to JavaScript objects; in other words, we create a Dart proxy object to the JavaScript object. To get the global object in JavaScript (which is mostly window
), use the top-level getter function context
; this is used in step 8. However, context
is also used to get the values of JavaScript variables, as shown in step 4.
You can create JavaScript objects as shown in step 5. Use the JsObject()
constructor. This takes the name of a JavaScript constructor function and the list of arguments that it needs as arguments. As shown in step 6, we can use the []
index operator to get the value of properties and []=
to set them; instead of a numerical index, we use the property name string as the key. The seventh and eighth step demonstrate that we can call a JavaScript method on an object with callMethod
, taking the name of the method and the list of its arguments as parameters. Finally, in step 9, we see that JsObject.jsify
turns a Dart map into a JavaScript object using the keys as properties; the same method also turns a Dart list into a JavaScript array.
To be able to compare, we will now show the same code but rewritten with the js
package. In js_interop2.html
, we have the same JavaScript, but running together with the Dart script js_interop2.dart
. We add the js
package to our pubspec.yaml
file as js:any
, and let pub get
do its magic. To make the package available to our Dart script, we add the following code to js_interop2.dart
:
import 'package:js/js.dart' as js;
Rewriting the Dart code from js_interop.dart
gets us the following output:
void main() { // getting a variable: var dart = js.context['jsvar']; print(dart); // I want Dart // making objects: var pers1 = new js.Proxy(js.context.Person, ['An', 'female']); var pers2 = new js.Proxy(js.context.Person, ['John', 'male']); // accessing and setting properties: print(pers1.name); // prints the whole object: [An, female] pers1.name = 'Melissa'; // change name property print(pers1.name); // Melissa // calling methods: pers1.sayHello.call(); // window: hello, I am Melissa pers2.greeting.call(pers1); // window: I greet you Melissa // getting the global object in JavaScript via context js.context.alert('Hello from Dart via JavaScript'), // using jsify: var jsMap = js.map({'a': 1,'b': 2}); print(jsMap); // [object Object] var jsArray = js.array([1, 2, 3]); print(jsArray); // [1, 2, 3] }
The syntax is a bit easier than dart:js
but because the names in the js
package cannot be minified since it uses dart:mirrors
and noSuchMethod
, using this library can result in a noticeable increase in code size when compiled to JavaScript. If this is a big disadvantage for you, use dart:js
instead. We use the js
package in the next recipe to talk to the Google Visualizations API.