get
Specify what happens when a certain property is read on a map. get
functions
work like a compute and automatically update themselves when a dependent
observable value is changed.
get( [lastSetValue] )
Defines the behavior when a value is read on a Map. Used to provide properties that derive their value from other properties of the map, or update their value from the changes in the value that was set.
Parameters
- lastSetValue
{*}
:The value last set by
.attr(property, value)
. Typically, lastSetValue should be an observable value, like a compute or promise. If it's not, it's likely that a define.set should be used instead.
Returns
{*}
:
The value of the property.
get( lastSetValue, setAttrValue(value) )
Asynchronously defines the behavior when a value is read on a Map. Used to provide property values that are available asynchronously.
Parameters
- lastSetValue
{*}
:The value last set by
.attr(property, value)
. - setAttrValue
{function(value)}
:Updates the value of the property. This can be called multiple times if needed.
Use
Getter methods are useful for:
- Defining virtual properties on a map.
- Defining property values that change with their internal set value.
Virtual properties
Virtual properties are properties that don't actually store any value, but derive their value from some other properties on the map.
Whenever a getter is provided, it is wrapped in a compute, which ensures that whenever its dependent properties change, a change event will fire for this property also.
var Person = Model.extend({
define: {
fullName: {
get: function () {
return this.attr("first") + " " + this.attr("last");
}
}
}
});
var p = new Person({first: "Justin", last: "Meyer"});
p.attr("fullName"); // "Justin Meyer"
p.bind("fullName", function(ev, newVal){
newVal //-> "Lincoln Meyer";
});
p.attr("first","Lincoln");
Asyncronous virtual properties
Often, a virtual property's value only becomes available after some period of time. For example,
given a personId
, one might want to retrieve a related person:
var AppState = Map.extend({
define: {
person: {
get: function(lastSetValue, setAttrValue){
Person.findOne({id: this.attr("personId")})
.then(function(person){
setAttrValue(person);
});
}
}
}
});
Asyncronous properties should be bound to before reading their value. If
they are not bound to, the get
function will be called each time.
The following example will make multiple Person.findOne
requests:
var state = new AppState({personId: 5});
state.attr("person") //-> undefined
// called sometime later ...
state.attr("person") //-> undefined
However, by binding, the compute only reruns the get
function once personId
changes:
var state = new AppState({personId: 5});
state.bind("person", function(){})
state.attr("person") //-> undefined
// called sometime later
state.attr("person") //-> Person<{id: 5}>
A template like stache will automatically bind for you, so you can pass
state
to the template like the following without binding:
var template = stache("<span>{{person.fullName}}</span>");
var state = new AppState({});
var frag = template(state);
state.attr("personId",5);
frag.childNodes[0].innerHTML //=> ""
// sometime later
frag.childNodes[0].innerHTML //=> "Lincoln Meyer"
The magic tags are updated as personId
, person
, and fullName
change.
Properties values that change with their internal set value
A getter can be used to derive a value from a set value. A getter's
lastSetValue
argument is the last value set by attr.
For example, a property might be set to a compute, but when read, provides the value of the compute.
var MyMap = Map.extend({
define: {
value: {
get: function( lastSetValue ){
return lastSetValue();
}
}
}
});
var map = new MyMap();
var compute = can.compute(1);
map.attr("value", compute);
map.attr("value") //-> 1
compute(2);
map.attr("value") //-> 2
This technique should only be used when the lastSetValue
is some form of
observable, that when it changes, can update the getter
value.
For simple conversions, set or type should be used.
Updating the virtual property value
It's very common (and better performing) to update the virtual property value instead of replacing it.
The following example creates an empty locationIds
List when a new
instance of Store
is created. However, as locations
change,
the List will be updated with the id
s of the locations
.
var Store = Map.extend({
define: {
locationIds: {
Value: Map.List,
get: function(initialValue){
var ids = [];
this.attr('locations').each(function(location){
ids.push(location.attr("id"));
});
return initialValue.replace(ids);
}
}
}
});