(this one we already know)
Observable state primitives
abstract Observable<T> {
// readable value
var value(get, never):T;
// a way to bind/unbind value change callbacks
function bind(handler:Callback<T>):CallbackLink;
// magic \o/ (more on this later)
static function auto<X>(f:()->X):Observable<X>;
}
abstract State<T> to Observable<T> { // also is an Observable
// writable value
var value(get, set):T;
// created with initial value
function new(value:T):Void;
// also bindable
function bind(handler:Callback<T>):CallbackLink;
}
// Some observable state
var messages = new ObservableArray([
{isRead: new State(true)},
{isRead: new State(false)},
{isRead: new State(false)},
]);
// Observable from a "computation"
var unreadCount = Observable.auto(() -> {
var count = 0;
for (message in messages) {
if (!message.isRead.value) {
count++;
}
}
return count;
});
// Example
trace(unreadCount.value); // 2
messages.push({isRead: new State(false)});
messages.push({isRead: new State(false)});
messages[1].isRead.value = true;
trace(unreadCount.value); // 3
(no macros involved, just smart invalidation management)
Macro-built model classes
class Player implements Model {
@:constant var id:Int;
@:editable var name:String;
}
class Player implements Model {
@:isVar public var id(get, never):Int;
public var name(get, set):String;
public final observables:{id:Observable<Int>, name:State<String>};
final __coco_name:State<String>;
public function new(__coco_init:{name:String, id:Int}) {
this.id = __coco_init.id;
this.__coco_name = new State(__coco_init.name);
}
inline function get_id() {
return this.id;
}
inline function get_name() {
return this.__coco_name.value;
}
function set_name(param:String) {
this.__coco_name.set(param);
return param;
}
}
source
output (simplified)
class Totals extends View {
@:attribute var count:Int;
function render() '
<TextField text=${"You have " + count + " resources"}/>
';
}