Класс basis.data.Value
(docs) и его потомки предназначены для хранения атомарных (не делимых) значений. Даже если значение имеет сложную структуру, например, объект, то изменения в его структуре не отслеживаются, и сам объект воспринимается как единое целое.
От basis.data.Value
образуются другие полезные классы, которые описываются в пространствах имен basis.data.value
и basis.data.index
.
Значение хранится в свойстве value
и может иметь любой тип. Его можно задать при создании объекта или используя метод set
. Если значение меняется (для сравнения используется ===
), то метод set
возвращает true
и выбрасывается событие change
. Обработчику события change
передается предыдущее значение, которое было до изменения.
У события
change
до версии1.0.0
была другая сигнатура: вторым параметром (перед oldValue) передавалось текущее значение объекта. Это не имело смысла, так как это значение доступно в свойствеvalue
и было убрано в1.0.0
.
var Value = basis.require('basis.data').Value;
var value = new Value({
value: 1,
handler: {
change: function(sender, oldValue){ // до 1.0.0 передавались параметры: sender, value, oldValue
console.log('value changed', oldValue, '->', this.value);
}
}
});
value.set(2);
// console> value changed 1 -> 2
// value.set(2) вернет true
value.set(2);
// console> false
У Value
есть свойство initValue
, которое хранит значение, назначенное объекту при создании. Метод reset
меняет текущее значение на значение свойства initValue
.
var Value = basis.require('basis.data').Value;
var value = new Value({
value: 1,
handler: {
change: function(sender, oldValue){
console.log('value changed', oldValue, '->', this.value);
}
}
});
value.set(2);
// console> value changed 1 -> 2
value.reset();
// console> value changed 2 -> 1
Когда требуется произвести множество изменений, можно заблокировать объект методом lock
. При этом значение будет изменяться, но событий выбрасываться не будет. Это нужно для того, чтобы минимизировать количество событий. Для разблокировки объекта используется метод unlock
, при этом сравнивается текущее значение и значение, которое было до блокировки, и если они отличаются - выбрасывается событие change
.
var Value = basis.require('basis.data').Value;
var value = new Value({
value: 0,
handler: {
change: function(sender, oldValue){
console.log('value changed', oldValue, '->', this.value);
}
}
});
for (var i = 0; i < 3; i++)
value.set(i + 1);
// console> value changed 0 -> 1
// console> value changed 1 -> 2
// console> value changed 2 -> 3
value.reset();
// console> value changed 3 -> 0
value.lock();
for (var i = 0; i < 3; i++)
value.set(i + 1);
value.unlock();
// console> value changed 0 -> 3
Часто нужно не текущее значение экземпляра Value
, а преобразованное по некоторому правилу, с возможностью отслеживать эти изменения. Для этого используются методы as
и deferred
Метод as
возвращает экземпляр basis.Token
(подробнее), который хранит преобразованное значение. Метод принимает один параметр:
var Value = basis.require('basis.data').Value;
var example = new Value({
value: 1
});
var doubleValue = example.as(function(value){
return value * value;
});
console.log(doubleValue);
// console> [object basis.Token]
console.log(doubleValue.value);
// console> 1
example.set(2);
console.log(example.value);
// console> 2
console.log(doubleValue.value);
// console> 4
Для того, чтобы получить basis.DeferredToken
, необходимо вызвать метод deferred
полученного токена (подробнее):
var doubleValue = example.as(function(value){ .. }).deferred();
Для одних и тех же значений параметра fn
возвращается один и тот же токен.
var Value = basis.require('basis.data').Value;
var example = new Value({
value: 1
});
var double = function(){ .. };
console.log(example.as(double) === example.as(double));
Иногда нужно получать преобразование значение экземпляра Value
, которое также зависит от другого экземпляра basis.event.Emitter
. Для этого создается фабрика токенов - функция, которая возвращает basis.Token
для заданого экземпляра basis.event.Emitter
. Такая функция создается методом compute
. Этот метод принимает два аргумента:
events - список названий событий (необязательный); список представляется в виде массива строк (названий событий) или строкой, где названия событий разделены пробелом;
fn - функция вычисления значения; такая фунция получает два аргумента:
object - объект, для которого создан токен;
value - текущее значение экземпляра Value
, от которого образована фабрика токенов.
Значение для токена вычисляется при его создании и перевычисляется, когда меняется значение у экземпляра Value
или выбрасывается событие, которое указано в списке событий. В случае разрушения Value
или объекта разрушается и токен.
var Value = basis.require('basis.data').Value;
var DataObject = basis.require('basis.data').Object;
var example = new Value({
value: 2
});
var sum = example.compute('update', function(object, value){
return object.data.property * value;
});
var object = new DataObject({
data: {
property: 3
}
});
var token = sum(object);
console.log(token.value);
// console> 6
object.update({ property: 10 });
console.log(token.value);
// console> 20
example.set(10);
console.log(token.value);
// console> 100
Фабрики удобно использовать в биндингах basis.ui.Node
, когда нужно получать значение, которое зависит от некоторого внешнего значения.
var Node = basis.require('basis.ui').Node;
var Value = basis.require('basis.data').Value;
var commission = new Value({ value: 10 });
var list = new Node({
container: document.body,
childClass: {
template: '<div>amount: {amount}, commission: {commission}</div>',
binding: {
amount: 'data:amount',
commission: commission.compute('update', function(node, value){
return (node.data.amount * value / 100).toFixed(2);
})
}
},
childNodes: [
{ data: { amount: 12 } },
{ data: { amount: 342 } },
{ data: { amount: 251 } }
]
});
// <div>
// <div>amount: 12, commission: 1.20</div>
// <div>amount: 342, commission: 34.20</div>
// <div>amount: 251, commission: 25.10</div>
// </div>
commission.set(15);
// <div>
// <div>amount: 12, commission: 1.80</div>
// <div>amount: 342, commission: 51.30</div>
// <div>amount: 251, commission: 37.65</div>
// </div>