Dart Js Wrapping

This package allows developers to define well-typed interfaces for JavaScript objects. Typed JavaScript Interfaces are classes that describes a JavaScript object and have a well-defined Dart API, complete with type annotations, constructors, even optional and named parameters.

Writing Js wrapper

Here's a quick example to show the package in action.

Given a JS class like :

LatLng = function(lat, lng) {
  this.lat = lat;
  this.lng = lng;
}
LatLng.prototype.equals = function(other) {
  return this.lat === other.lat && this.lng === other.lng;
}

You can create a wrapper like :

@JsName()
abstract class LatLng {
  factory LatLng(num lat, num lng, [bool noWrap]) => $js;

  bool equals(LatLng other);
  num get lat => _lat();
  @JsName('lat')
  num _lat();
  num get lng => _lng();
  @JsName('lng')
  num _lng();
  String toString();
  String toUrlValue([num precision]);
}

Once the generator executed you will be able to use a LatLng that wraps a js LatLng.

Configuration and Initialization

Adding the dependency

Add the following to your pubspec.yaml:

dependencies:
  js_wrapping: ^0.6.0
dev_dependencies:
  js_wrapping_generator: ^0.6.0

Running the generator

See the Running generators section of the source_gen package.

Usage

Warning: The API is still changing rapidly. Not for the faint of heart

Defining Typed JavaScript Interfaces

To create a Typed JavaScript Interface you will start by creating a class marked with @JsName(). It will be the template used to create a js interface.

@JS('google.maps')
library google_maps.sample.simple;

import 'package:js_wrapping/js_wrapping.dart';

@JsName()
abstract class LatLng {
  factory LatLng(num lat, num lng, [bool noWrap]) => $js;

  bool equals(LatLng other);
  num get lat => _lat();
  @JsName('lat')
  num _lat();
  num get lng => _lng();
  @JsName('lng')
  num _lng();
  String toString();
  String toUrlValue([num precision]);
}

The generator will provide a new library mylib.g.dart containing :

// GENERATED CODE - DO NOT MODIFY BY HAND

// **************************************************************************
// JsWrappingGenerator
// **************************************************************************

// Copyright (c) 2015, Alexandre Ardhuin. All rights reserved. Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

@JS('google.maps')
library google_maps.sample.simple;

import 'package:js_wrapping/js_wrapping.dart';

@JS()
class LatLng {
  external LatLng(num lat, num lng, [bool noWrap]);

  external bool equals(LatLng other);

  external String toString();

  external String toUrlValue([num precision]);
}

extension LatLng$Ext on LatLng {
  num get lat => _lat();
  num get lng => _lng();

  num _lat() => callMethod(this, 'lat', []);

  num _lng() => callMethod(this, 'lng', []);
}

Constructors to create js object

If LatLng is a js object/function you can create a new instance in js with LatLng(). To make it possible to create such js instance from the Dart-side you have to define a factory constructor:

@JsName()
abstract class LatLng {
  factory LatLng(num lat, num lng, [bool noWrap]) => $js;
}

This will provide:

@JS()
class LatLng {
  external LatLng(num lat, num lng, [bool noWrap]);
}

It's now possible to instantiate js object from Dart with LatLng().

Properties and accessors

Properties or abstract getters/setters can be added to the class and will generate getters and setters to access to the properties of the underlying js object.

@JsName()
abstract class Person {
  factory Person() => $js;
  String firstname, lastname;
  int get age;
  void set email(String email);
}

This will provide:

@JS()
class Person {
  external Person();
  external String get firstname;
  external set firstname(String value);
  external String get lastname;
  external set lastname(String value);
  external int get age;
  external void set email(String email);
}

Methods

The abstract methods will be implemented the same way :

@JsName()
abstract class Person {
  factory Person() => $js;
  String sayHelloTo(String other);
  void fall();
}

This will provide:

@JS()
class Person {
  external Person();

  external String sayHelloTo(String other);

  external void fall();
}

Names used

constructors

You can override this name by providing a JsName('MyClassName') on the class.

@JsName('People')
abstract class Person {
  factory Person() => $js;
  String sayHelloTo(Person other);
  Person get father;
}

members

You can override this name by providing a JsName('myMemberName') on the member.

@JsName()
abstract class Person {
  @JsName('daddy') Person get father;
}

Tips & Tricks

anonymous objects

It's common to instantiate anonymous Js object. If your private classe maps an anonymous object you can add @anonymous on it.

@JsName()
@anonymous
abstract class Foo {
  factory Foo() => $js;
}

This generates:

@JS()
@anonymous
class Foo {
  external factory Foo();
}

create getter from method

If a js object as a getXxx() function you would like to map on the dart side with a get xxx you can do something like that:

abstract class Person {
  String get firstname => _getFirstname();
  String _getFirstname();
}
@JsName()
abstract class Person {
  factory Person() => $js;

  String get firstname => _getFirstname();
  @JsName('getFirstname')
  String _getFirstname();
}

This can be applied to any redirection you'd like to do.

Libraries

js_wrapping
The js library allows Dart library authors to define Dart interfaces for JavaScript objects.