Smart & Sweet cross-platform data-binding impl. on Signals.
- 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:
implements hx.binding.IBindable
;- Mark fields with
@:bindable
meta; - Now this fields can give you onChange-signal via:
field!
Haxe 3.3 syntax (postfix!
);field.signal()
standard syntax; - Use mixin
using hx.binding.Tool
for fast bindinga to b
; - Bind it to any expression
a!.bind(b)
, whereb
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
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
Iffalse
/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:
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:
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 it42
)-
Look, method
bind
is amacro
function from mixinhx.binding.Tool
. It will return simple expression where in same context we adding listener to the associated signal. In the output the expressiond.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.