rp (ReactiveProperty)
rp = value containers + computed values + automatic dependency tracking
- works well with objects and arrays
- including common computations
Basics
var rp = ;// Creating RPsvar x = rp; // read/writablevar y = rp; // only readable// Reading values=== 1=== 2y === 2 // y() is a shortcut for y.get()// Computed RPsvar xy = rp;=== 3// Writing valuesx;=== 5// Computed RPs are updated=== 6
Reading operations
Shortcuts for common computations. They all return a RP so they are chainable.
var x = rp;var y = rp;x; // => 11.869...x // => falsex // => truex // => 3.14...x // => "3.14..."x // => 3x // => 3x // => 4x // => 126.14...y // => 119.86...y // => 39.15...x // => 386.41...x // falsex // -1x // -1rp // => ["test", "TEST"]rp // => [1,2]rp // => "[1,2]"
// Array operationsvar z = rp;var a = rp;z// => [2, 4, 6, 8]z// => [1, 3]z// => 10z // => [2, 3]z // => 0zsize // => 4
Writing operations
var x = rp;var y = rp;var z = rp;x; // x = x + 1x; // x = x - 1x; // x = x + 3;x; // x = x - 6;x; // x = x * 10;x; // x = x / y;z;z;z; // => 2z; // => 2z;;
Helpers
var z = rp;z;z; // logs all changes with the name "z"z; // readonly version of anythingz; // cannot be updated, but elements and properties can be changedz; // cannot be updated, neither can elements be added or removed// But values can be changedz === falserp === true// Returns true is the value is constant
Objects and Arrays
var o = rp;// Getting RP of properties and elementsvar a = ;var b = ;var c = ;var d = ;var p = ;=== 1=== 2// not existing properties are undefined=== undefined// RP move with array modificationsb;// d is now o("a", "b", 0, "d")=== 2// removed elements become undefined=== undefined// you get the same instance for a property or element=== d
Memory management
// Every RP is only valid until the next tickvar x = rp;// fineprocess;// To make them longer valid put them into a scopevar x = rpscope; // into the global scope// or into a private scopevar s = rpscope {var x = rpscope;};// continue to run code in this scopes;sscope {var a = rpscope;};// Scope[ x, y, Scope[ z ], Scope[ a ] ]// unref the scope anytime you want// nested scopes are also removeds;// Computed forms a scope too// but it's not part of the parent scope// It's handled by the normal RP lifetimevar x = rp;// x is valid until the next tick, so is y
Handled get
Read the value, but handle changes by some function.
rpprototyperpprototype
A handled get is active as long as the current scope is active.
Example: exchange the HTML code of a HTMLElement:
var x = rp;var tag = rp;var html = x;var element = rp;var el1 = ;x;=== el1;tag;!== el1;
Atomic
var x = rp;var counter = 0;var y = x;=== "test1"// atomic delay updates to the end of the block// can boost performance for many changesrp;=== "c2"
Delegated
var a = rp;var b = rp;var which = rp;// while computed RPs provide only read accessvar y = which;// delegated RPs can provide read/write access// to a dynamically choosen RPvar ref = which;var x = rp;// or: var x = ref.delegated();// or: var x = rp.delegated(function() { return which() ? a : b; });=== 2x;=== 4which;=== 1s;=== 9
Two-way computions
var x = rp;var y = x;=== 2y;=== 25x;=== 4
Two-way operations
var x = rp;var str1 = x;var str2 = x;=== "123"str1;=== 456x;=== "789"=== "789"str2;=== 123
Attributes
Every RP can have attributes. One can use them to exchange user data. Each attribute is a rp.variable
.
var x = rp;{var a = ;a;};var aValid = ;=== false;;=== true;=== true;x === false;
Internals
All works with 5 events:
changed
: The value changedupdated
: The primitive value changed or the reference changedadded
: An item is added to the arrayremoved
: An item is removed from the arraynested
: A event occured on nested stuff
var a = rp;var b = rp;var c = rp;var d = rp;a;// a updated(123, /test/)// a changed()a;// no eventsb;// b added(2/*idx*/, 3/*value*/)// b changed()b;// b updated([], [1,2,3])// b changed;// c nested("updated", ["a"], 2, 1);// c changedc;// c updated({a: 2}, {a: 2});// c changed;// c nested("added", ["a", "b"], 2);// d changed;// c nested("updated", ["a", "b", 1], 3);// d changed
The nested event is not used internally, can be used to capture all updates on objects and arrays.
Computed values listen on "changed", "updated", "added" and "removed" and update their values.
By design they could do this very clever:
var x = rp;var y = x;// y() is now [4, 16]x;// no events for yx// y added(2, 36)
But it's work on progress for some function. (They currently update the whole array, where a bunch of "added" or "removed" events would do it better.)
// You can listen on the eventsx;x;x;x;x;x;x;x;// ...x;
Internally each RP uses reference counting to know when to dispose itself. At creation they get one reference that is removed at nextTick. Disposing means to remove event listener from RPs it depend on.
With x.ref()
and x.unref()
you can add/remove references, but there is no need to do this if you don't want to write your own RP. Better use x.scope()
and let the scope manage your RPs.
TODO
Test status
License
MIT