Smart & Sweet data-binding impl. on Signals.

To install, run:

haxelib install bindings 0.9.3 

See using Haxelib in Haxelib documentation for more information.

Smart & Sweet cross-platform data-binding impl. on Signals.

Build status

  • highly macro-powered
  • with minimal overhead


Compatible with targets:

  • Flash
  • Java
  • Neko
  • C++
  • PHP
  • JS
  • Python
  • Lua

C# in future plans.


Stable release: haxelib install bindings.

Development version:

haxelib hg bindings

Then add to your hxml: -lib bindings.


  1. implements hx.binding.IBindable;
  2. Mark fields with @:bindable meta;
  3. Now this fields can give you onChange-signal via: field! Haxe 3.3 syntax (postfix !); field.signal() standard syntax;
  4. Use mixin using hx.binding.Tool for fast binding a to b;
  5. Bind it to any expression a!.bind(b), where b is any possible expression;

haxe using hx.binding.Tool;

class Example implements hx.binding.IBindable {

@:bindable public static var one:String = "one";
@:bindable var two:String = "two";
@:bindable var three(default, null):String = "three";

public function new()
	// one => two
	// two => three
	// three => one (Yeeeh, cycle!)

	// set new value:
	one = "new value";

	trace(one); // "new value"
	trace(two); // "new value"
	trace(three); // "new value"

	// --- //

	// add watcher / listener:
	function watcher(value)
		trace('watcher: new value: $value');

	// set new value:
	one = "42"; // "watcher: new value: 42"
	two = "WOW!"; // "watcher: new value: WOW!"



  • binding-strict If false/0 or not defined then more validation for bindings and inner assignments;
  • binding-limit Maximum limit for total num of bindable fields, e.g.: -D binding-limit=100. Please don't use!
  • binding-debug Prints all bindable fields and all bindings; -D binding-debug=warn Make warnings for all bindable fields and all bindings instead print it; -D binding-debug=line Print bindings sorted by line of expression; * -D binding-debug (default) Print bindings sorted by adding order (depending on the compiler).

Example binding-debug output:

log Bindings initiated in module ./test/MixinToolTest.hx: 1 / 10 : (line: 25) Foo.fieldA! -> data.fieldA 2 / 10 : (line: 26) data.fieldB! -> Foo.fieldB 3 / 10 : (line: 61) Foo.fieldA! -> localA 4 / 10 : (line: 109) Foo.fieldA.signal() -> data.fieldA 5 / 10 : (line: 110) data.fieldB.signal() -> Foo.fieldB

Bindings initiated in module ./test/BindingTest.hx: 6 / 10 : (line: 164) impl.myPrivateLocalField! -> impl.myPublicLocalField 7 / 10 : (line: 165) impl.myPublicLocalField! -> impl.myPrivateLocalField 8 / 10 : (line: 212) myStaticField -> myLocalNoneBindableField 9 / 10 : (line: 226) myLocalField <-> myStaticField 10 / 10 : (line: 277) myLocalField! <-> impl.myPrivateLocalField

Example binding-debug=warn output:

screenshot binding-debug=warn

How it works?

You create a variable anywhere. For example you have a class: haxe class Data implements IBindable {

@:bindable public var myBindableField:String;
public function new(){}


Next you have an instance of it and want to bind field:

haxe using hx.binding.Tool;

class Anywhere {

var enotherField:String;

public function foo()
	var d = new Data();
	// bind it:

	// and now any value assigned to myBindableField will assign to enotherField too.


There is two key moments:

  • myBindableField! will transformed to "get my associated signal by ID" -> hx.binding.BindableFields.get(42), where ID is inlined integer (in this example it 42)
  • Look, method bind is a macro function from mixin hx.binding.Tool. It will return simple expression where in same context we adding listener to the associated signal. In the output the expression d.myBindableField!.bind(enotherField); will look like to next code: haxe hx.binding.BindableFields.get(42).add( function(value:String) {

    enotherField = value;


  • Because class Data implements IBindable all fields with meta @:bindable will transformed: type will wrapped to abstract type with generated inlinable id of field; for field will created inlinable setter: haxe function set_myBindableField(value:String) if(value != myBindableField) {

    myBindableField = value;
    myBindableField!.dispatch(value); // will transformed to next line:
    // hx.binding.BindableFields.get(42).dispatch(value); // why - see the first moment.


That is all.

Strict typing bonus - cool completion by compiler:

ScreenShot about completion

ScreenShot about completion

ScreenShot about completion

2 years ago

All libraries are free

Every month, more than thousand developers use haxelib to find, share, and reuse code — and assemble it in powerful new ways. Enjoy Haxe; It is great!

Explore Haxe

Haxe Manual

Haxe Code Cookbook

Haxe API documentation

You can try Haxe in the browser!

Join us on Github!

Haxe is being developed on GitHub. Feel free to contribute or report issues to our projects.

Haxe on Github