UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

195 lines (139 loc) 5.19 kB
@function can.Map.prototype.define.get get @parent can.Map.prototype.define Specify what happens when a certain property is read on a map. `get` functions work like a [can.compute] and automatically update themselves when a dependent observable value is changed. @signature `get( [lastSetValue] )` Defines the behavior when a value is read on a [can.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. @param {*} [lastSetValue] The value last set by `.attr(property, value)`. Typically, _lastSetValue_ should be an observable value, like a [can.compute] or promise. If it's not, it's likely that a [can.Map.prototype.define.set define.set] should be used instead. @return {*} The value of the property. @signature `get( lastSetValue, setAttrValue(value) )` Asynchronously defines the behavior when a value is read on a [can.Map]. Used to provide property values that are available asynchronously. @param {*} lastSetValue The value last set by `.attr(property, value)`. @param {function(*)} setAttrValue(value) Updates the value of the property. This can be called multiple times if needed. @body ## 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 [can.compute], which ensures that whenever its dependent properties change, a change event will fire for this property also. ``` var Person = can.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 = can.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 [can.stache] will automatically bind for you, so you can pass `state` to the template like the following without binding: ``` var template = can.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 [can.Map::attr]. For example, a property might be set to a compute, but when read, provides the value of the compute. ``` var MyMap = can.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, [can.Map.prototype.define.set] or [can.Map.prototype.define.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` [can.List] when a new instance of `Store` is created. However, as `locations` change, the [can.List] will be updated with the `id`s of the `locations`. ``` var Store = can.Map.extend({ define: { locationIds: { Value: can.List, get: function(initialValue){ var ids = []; this.attr('locations').each(function(location){ ids.push(location.attr("id")); }); return initialValue.replace(ids); } } } }); ```