@function can-define.types.value value @parent can-define.behaviors
Specify the behavior of a property by listening to changes in other properties.
@signature value(resolve, listenTo, stopListening)
The value
behavior is used to compose a property value from events dispatched
by other properties on the map. value
enables techniques very similar to using
event streams and functional reactive programming. When the property is
bound for the first time, value
will be called with
resolve
, listenTo
, and stopListening
. Use listenTo
to listen to events
dispatched on the map or other observables,
stopListening
to stop listening to those events if needed, and resolve
to set a new value on the observable. For example, the following
counts the number of times the name
property changed:
Person = DefineMap.extend("Person",{
name: "string",
nameChangeCount: {
value(resolve, listenTo){
var count = 0;
listenTo("name", () => {
resolve(++count);
});
resolve(count);
}
}
});
var p = new Person();
p.on("nameChangedCount", (ev, newVal)=> {
console.log("name changed", newVal, "times");
});
p.name = "Justin" // logs name changed 1 times
p.name = "Ramiya" // logs name changed 2 times
@param {function(Any)} resolve(value) Sets the value of this property as value
.
@param {function(Any,String,Fuction)} listenTo(bindTarget,event,handler) abc.
@param {function(Any,String,Fuction)} stopListening(bindTarget,event,handler) abcd.
@return {function} An optional teardown function. If provided, the teardown function will be called when the property is unbound.
@body
The value
behavior should be used where the [can-define.types.get] behavior can
not derive a property value from instantaneous values. This often happens in situations
where the fact that something changes needs to saved in the state of the application.
Lets first see an example where [can-define.types.get] should be used, the
ubiquitous fullName
property. The following creates a fullName
property
that derives its value from the instantaneous first
and last
values:
DefineMap.extend("Person", {
first: "string",
last: "string",
fullName: {
get() {
return this.first + " " + this.last;
}
}
});
[can-define.types.get] is great for these types of values. But [can-define.types.get] is unable to derive property values based on the change of values or the passage of time.
The following fullNameChangeCount
increments every time fullName
changes:
DefineMap.extend("Person", {
first: "string",
last: "string",
fullName: {
get() {
return this.first + " " + this.last;
}
},
fullNameChangeCount: {
value(resolve, listenTo){
var count = 0;
resolve(0);
listenTo("fullName", ()=> {
resolve(++count)
})
}
}
});
The following time
property increments every second:
var Timer = DefineMap.extend("Timer",{
time: {
value(resolve) {
resolve(new Date());
var interval = setInterval(() => {
resolve(new Date())
},1000);
return () => {
clearInterval(interval);
};
}
}
});
var timer = new Timer();
timer.on("time", function(ev, newVal, oldVal){
console.log(newVal) //-> logs a new date every second
});
// how do you stop an interval? // lets just "kill" if there's no binding at the end of this .... // temporarily bind