HTTPS SSH

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

Build status

  • highly macro-powered
  • with minimal overhead

Compatibility

Compatible with targets:

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

C# in future plans.

Install:

Stable release: haxelib install bindings.

Development version:

haxelib hg bindings https://bitbucket.org/fzzr/hx.bindings

Then add to your hxml: -lib bindings.

Usage:

  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;
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
        one!.bind(two);
        // two => three
        two!.bind(three);
        // three => one (Yeeeh, cycle!)
        three!.bind(one);

        // 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');
        two!.add(watcher);

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

Defines:

  • 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:

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:

class Data implements IBindable
{
    @:bindable public var myBindableField:String;
    public function new(){}
}

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

using hx.binding.Tool;

class Anywhere
{
    var enotherField:String;

    public function foo()
    {
        var d = new Data();
        // bind it:
        d.myBindableField!.bind(enotherField);

        // 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:
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:
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