js_wrapping 0.4.1 copy "js_wrapping: ^0.4.1" to clipboard
js_wrapping: ^0.4.1 copied to clipboard

outdatedDart 1 only

That package contains elements to easilly wrap JS library in Dart.

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 :

// you define a private abstract class implementing/extending JsInterface
abstract class _LatLng implements JsInterface {

  // factories to create js objects
  factory _LatLng(num lat, num lng) => null;

  // uninitialized fields to bind to properties
  num lat, lng;

  // abstract methods to call Js methods
  bool equals(LatLng other);
}

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.4.0
dev_dependencies:
  js_wrapping_generator: ^0.4.0

Adding the generator #

A generator is used to automatically implement the JsInterface you will create. You have to configure a tool/phases.dart file. You can adapt the file used in google_maps. 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 private class that extends or implements JsInterface. It will be the template used to create a really class that wrap the underlying JsObject.

import 'package:js_wrapping/js_wrapping.dart';

part 'mylib.g.dart'; // assuming the current file is 'mylib.dart'

abstract class _Foo implements JsInterface {
}

The generator will provide the part mylib.g.dart containing :

// **************************************************************************
// Generator: Instance of 'JsInterfaceGenerator'
// Target: abstract class _Foo
// **************************************************************************

class Foo extends JsInterface implements _Foo {
  Foo.created(JsObject o) : super.created(o);
}

The contructor created allows to wrap existing JsObject.

Constructors to create js object #

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

abstract class _Foo implements JsInterface {
  factory _Foo() => null;
}

This will provide:

class Foo extends JsInterface implements _Foo {
  Foo.created(JsObject o) : super.created(o);
  Foo() : this.created(new JsObject(context['Foo']));
}

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

NB: You can also use named constructors.

Properties and accessors #

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

abstract class _Person implements JsInterface {
  String firstname, lastname;
  int get age;
  void set email(String email);
}

This will provide:

class Person extends JsInterface implements _Person {
  Person.created(JsObject o) : super.created(o);

  void set lastname(String _lastname) {
    asJsObject(this)['lastname'] = _lastname;
  }
  String get lastname => asJsObject(this)['lastname'];
  void set firstname(String _firstname) {
    asJsObject(this)['firstname'] = _firstname;
  }
  String get firstname => asJsObject(this)['firstname'];
  int get age => asJsObject(this)['age'];
  void set email(String email) {
    asJsObject(this)['email'] = email;
  }
}

NB: asJsObject(this) is used to get the underlying JsObject and perform operations on it.

Methods #

The abstract methods will be implemented the same way :

abstract class _Person implements JsInterface {
  String sayHelloTo(String other);
  void fall();
}

This will provide:

class Person extends JsInterface implements _Person {
  Person.created(JsObject o) : super.created(o);

  String sayHelloTo(String other) =>
      asJsObject(this).callMethod('sayHelloTo', [other]);
  void fall() {
    asJsObject(this).callMethod('fall');
  }
}

Parameters types and return types #

The generation relies on the type annotations provided. If you use a JsInterface as return type the generator will automatically wrap the underlying js object in the indicated type. You are also allowed to use JsInterface as parameters.

For instance:

abstract class _Person implements JsInterface {
  String sayHelloTo(Person other);
  Person get father;
}

This will provide:

class Person extends JsInterface implements _Person {
  Person.created(JsObject o) : super.created(o);

  String sayHelloTo(Person other) =>
      asJsObject(this).callMethod('sayHelloTo', [__codec2.encode(other)]);
  Person get father => __codec2.decode(asJsObject(this)['father']);
}
/// codec for Person
final __codec2 = new JsInterfaceCodec<Person>((o) => new Person.created(o));

Note that in sayHelloTo other is unwrapped with toJs automatically. In get father a new Person object is created.

NB: returning Lists and using them as parameters are also supported.

Names used #

constructors

By default the names used for object instantiation are the name minus the prepended _. Thus a class _Foo will use the js function/class Foo. You can override this name by providing a JsName('MyClassName') on the class.

@JsName('People')
abstract class _Person implements JsInterface {
  String sayHelloTo(Person other);
  Person get father;
}

members

By default the name used for the call is the member's name if public or the name minus the prepended _ if private. Thus the methods m1() and _m1() will use the same js function m1. You can override this name by providing a JsName('myMemberName') on the member.

abstract class _Person implements JsInterface {
  @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.

@anonymous
abstract class _Foo implements JsInterface {
  factory _Foo() => null;
}

This generates:

@anonymous
class Foo extends JsInterface implements _Foo {
  Foo.created(JsObject o) : super.created(o);
  Foo() : this.created(new JsObject(context['Object']));
}

Note the context['Object'] used on creation.

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 implements JsInterface {
  String get firstname => _getFirstname();
  String _getFirstname();
}

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

avoid to repeat a namespace on every classes

You can add a JsName('my.namespace') on your library. Thus every constructor will prepend the name of the class with this name.

@JsName('my.namespace')
library familly;
abstract class _Person implements JsInterface {
  factory _Person() => null;
}

This generates:

class Person extends JsInterface implements _Person {
  Person.created(JsObject o) : super.created(o);
  Person() : this.created(new JsObject(context['my']['namespace']['Person']));
}
6
likes
0
pub points
63%
popularity

Publisher

unverified uploader

That package contains elements to easilly wrap JS library in Dart.

Repository (GitHub)
View/report issues

License

unknown (license)

More

Packages that depend on js_wrapping