value
Specify the behavior of a property by listening to changes in other properties.
value(prop)
The value behavior is used to compose a property value from events dispatched
by other properties on the map. It's similar to get, but can
be used to build property behaviors that get can not provide.
value enables techniques very similar to using event streams and functional
reactive programming. Use prop.listenTo to listen to events dispatched on
the map or other observables, prop.stopListening to stop listening to those
events if needed, and prop.resolve to set a new value on the observable.
For example, the following counts the number of times the name property changed:
import {DefineMap} from "can";
const Person = DefineMap.extend( "Person", {
name: "string",
nameChangeCount: {
value( prop ) {
let count = 0;
prop.listenTo( "name", () => {
prop.resolve( ++count );
} );
prop.resolve( count );
}
}
} );
const p = new Person();
p.on( "nameChangeCount", ( ev, newValue ) => {
console.log( "name changed " + newValue + " times." );
} );
p.name = "Justin"; // logs name changed 1 times
p.name = "Ramiya"; // logs name changed 2 times
If the property defined by value is unbound, the value function will be called each time. Use prop.resolve synchronously
to provide a value.
type, default, get, and set behaviors are ignored when value is present.
value properties are not enumerable by default.
Parameters
- prop
{ValueOptions}:An object of methods and values used to specify the property behavior:
prop.resolve(value)
{function(Any)}Sets the value of this property asvalue. During a batch, the last value passed toprop.resolvewill be used as the value.prop.listenTo(bindTarget, event, handler, queue)
{function(Any,String,Fuction,String)}A function that sets up a binding that will be automatically torn-down when thevalueproperty is unbound. Thisprop.listenTomethod is very similar to the listenTo method available on DefineMap. It differs only that it:- defaults bindings within the notifyQueue.
- calls handlers with
thisas the instance. - localizes saved bindings to the property instead of the entire map.
Examples:
// Binds to the map's `name` event: prop.listenTo( "name", handler ); // Binds to the todos `length` event: prop.listenTo( todos, "length", handler ); // Binds to the `todos` `length` event in the mutate queue: prop.listenTo( todos, "length", handler, "mutate" ); // Binds to an `onValue` emitter: prop.listenTo( observable, handler );prop.stopListening(bindTarget, event, handler, queue)
{function(Any,String,Fuction,String)}A function that removes bindings registered by theprop.listenToargument. Thisprop.stopListeningmethod is very similar to the stopListening method available on DefineMap. It differs only that it:- defaults to unbinding within the notifyQueue.
- unbinds saved bindings by
prop.listenTo.
Examples:
// Unbind all handlers bound using `listenTo`: prop.stopListening(); // Unbind handlers to the map's `name` event: prop.stopListening( "name" ); // Unbind a specific handler on the map's `name` event // registered in the "notify" queue. prop.stopListening( "name", handler ); // Unbind all handlers bound to `todos` using `listenTo`: prop.stopListening( todos ); // Unbind all `length` handlers bound to `todos` // using `listenTo`: prop.stopListening( todos, "length" ); // Unbind all handlers to an `onValue` emitter: prop.stopListening( observable );prop.lastSet
{can-simple-observable}An observable value that gets set when this property is set. You can read its value or listen to when its value changes to derive the property value. The following makespropertybehave like a normal object property that can be get or set:import {DefineMap} from "can"; const Example = DefineMap.extend( { property: { value: function( prop ) { console.log( prop.lastSet.get() ); //-> "test" // Set `property` initial value to set value. prop.resolve( prop.lastSet.get() ); // When the property is set, update `property`. prop.listenTo( prop.lastSet, prop.resolve ); } } } ); const e = new Example(); e.property = "test"; e.serialize();
Returns
{function}:
An optional teardown function. If provided, the teardown function
will be called when the property is unbound after stopListening() is used to
remove all bindings.
The following time property increments every second. Notice how a function
is returned to clear the interval when the property is returned:
import {DefineMap} from "can";
const Timer = DefineMap.extend( "Timer", {
time: {
value( prop ) {
prop.resolve( new Date() );
const interval = setInterval( () => {
const date = new Date()
console.log( date.getSeconds() ); //-> logs a new date every second
prop.resolve( date );
}, 1000 );
return () => {
clearInterval( interval );
};
}
}
} );
const timer = new Timer();
timer.listenTo( "time", () => {} );
setTimeout( () => {
timer.stopListening( "time" );
}, 5000 ); //-> stops logging after five seconds
Use
The value behavior should be used where the 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.
Our next example shows how get should be used with the
fullName property. The following creates a fullName property
that derives its value from the instantaneous first and last values:
import {DefineMap} from "can";
const Person = DefineMap.extend( "Person", {
first: "string",
last: "string",
get fullName() {
return this.first + " " + this.last;
}
} );
const p = new Person({ first: "John", last: "Smith" });
console.log( p.fullName ); //-> "John Smith"
get is great for these types of values. But 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:
import {DefineMap} from "can";
const Person = DefineMap.extend( "Person", {
first: "string",
last: "string",
fullName: {
get() {
return this.first + " " + this.last;
}
},
fullNameChangeCount: {
value( prop ) {
let count = 0;
prop.resolve( 0 );
prop.listenTo( "fullName", () => {
prop.resolve( ++count );
} );
}
}
} );
const p = new Person({ first: "John", last: "Smith" });
p.on("fullNameChangeCount", () => {});
p.first = "Justin";
p.last = "Meyer";
console.log(p.fullNameChangeCount); //-> 2