CanJS's observables - can.Map, can.List,
and can.compute - let you make changes to data and listen
to those changes. Observables are the subjects in
the observer pattern. CanJS comes with
three forms of observables:
can.Map and can.List are often extended to create observable types.
Models and can.route are
based on can.Map, and can.Component's scope is a
can.Map, but observables are useful on their own too.
To create a Map, call new can.Map(obj). This will give you a map
with the same properties and values as obj. To create a List, call new can.List(array). This will give you a List with the same elements as
array.
var pagination = new can.Map({page: 1, perPage: 25, count: 1388});
pagination.attr('perPage'); // 25
var hobbies = new can.List(['programming', 'bball', 'party rocking']);
hobbies.attr(2); // 'partying'
Manipulating properties
The attr method is
used to read and write a property or properties from a Map or List.
When a property on a Map is changed with attr, the Map will emit two
events: A change event and an event with the same name as the property that
was changed. You can listen for these events by using
bind:
Extending a can.Map (or can.List) lets you create custom observable
types. The following extends can.Map to create a Paginate type that
has a .next() method to change its state:
As mentioned above, CanJS also provides observable arrays with can.List.
can.List inherits from can.Map, so a List works much the same way an
Map does, with the addition of several methods useful for working with
arrays:
unshift, which adds an item to the front
of a List.
splice, which removes and inserts items
anywhere in a List.
When these methods are used to modify a List, the appropriate events are
emitted. See the API for Lists for more
information on the arguments passed to those event handlers.
Computed values
CanJS also provides a way to make values themselves observable with
can.compute. A Compute represents a dynamic value
that can be read, set, and listened to just like a Map.
Static Computes
A simple static Compute contains a single value, and is created by calling
can.compute(value). This value can be read, set, and listened to:
// create a Compute
var age = can.compute(25),
previousAge = 0;
// read the Compute's value
age(); // 25
// listen for changes in the Compute's value
age.bind('change', function(ev, newVal, oldVal) {
previousAge = oldVal;
});
// set the Compute's value
age(26);
age(); // 26
previousAge; // 25
Composite Computes
Computes can also be used to generate a unique value based on values derived
from other observable properties. This type of compute is created by calling
can.compute(getterFunction). When the observable properties that the compute is
derived from change, the value of the compute changes:
var name = new can.Map({
first: 'Alice',
last: 'Liddell'
});
var fullName = can.compute(function() {
// We use attr to read the values
// so the compute knows what to listen to.
return name.attr('first') + ' ' + name.attr('last');
});
var previousName = '';
fullName(); // 'Alice Liddell'
fullName.bind('change', function(ev, newVal, oldVal) {
previousName = oldVal;
});
name.attr({
first: 'Allison',
last: 'Wonderland'
});
fullname(); // 'Allison Wonderland'
previousName; // 'Alice Liddell'
Since the value of the Compute is cached any time a derived value is
changed, reading the value is fast.
Converted Computes
Computes are also useful for creating links to properties within Observes. One
of the most frequent examples of this is when converting from one unit to
another.
// progress ranges from 0 to 1.
var project = new can.Map({ progress: 0.3 });
var progressPercentage = can.compute(function(newVal) {
if(newVal !== undefined) {
// set a value
project.attr('progress', newVal / 100);
} else {
// get the value
return project.attr('progress') * 100;
}
});
percentage(); // 30
// Setting percentage...
percentage(75);
// ...updates project.progress!
project.attr('progress'); // .75
CanJS's observables - can.Map, can.List, and can.compute - let you make changes to data and listen to those changes. Observables are the subjects in the observer pattern. CanJS comes with three forms of observables:
can.Map and can.List are often extended to create observable types. Models and can.route are based on can.Map, and can.Component's scope is a can.Map, but observables are useful on their own too.
To create a Map, call
new can.Map(obj)
. This will give you a map with the same properties and values as obj. To create a List, callnew can.List(array)
. This will give you a List with the same elements as array.Manipulating properties
The attr method is used to read and write a property or properties from a Map or List.
Properties can be removed from Observes with
removeAttr
, which is equivalent to thedelete
keyword:Listening to events
When a property on a Map is changed with
attr
, the Map will emit two events: A change event and an event with the same name as the property that was changed. You can listen for these events by using bind:You can similarly stop listening to these events by using unbind:
Iterating though a Map
If you want to iterate through the properties on a Map, use
each
:Extending a Map
Extending a can.Map (or can.List) lets you create custom observable types. The following extends can.Map to create a Paginate type that has a
.next()
method to change its state:Observable Arrays
As mentioned above, CanJS also provides observable arrays with can.List. can.List inherits from can.Map, so a List works much the same way an Map does, with the addition of several methods useful for working with arrays:
When these methods are used to modify a List, the appropriate events are emitted. See the API for Lists for more information on the arguments passed to those event handlers.
Computed values
CanJS also provides a way to make values themselves observable with can.compute. A Compute represents a dynamic value that can be read, set, and listened to just like a Map.
Static Computes
A simple static Compute contains a single value, and is created by calling
can.compute(value)
. This value can be read, set, and listened to:Composite Computes
Computes can also be used to generate a unique value based on values derived from other observable properties. This type of compute is created by calling
can.compute(getterFunction)
. When the observable properties that the compute is derived from change, the value of the compute changes:Since the value of the Compute is cached any time a derived value is changed, reading the value is fast.
Converted Computes
Computes are also useful for creating links to properties within Observes. One of the most frequent examples of this is when converting from one unit to another.