Класс basis.data.KeyObjectMap
представляет собой класс, чьи экземпляры хранят информацию об отношении некоторого значения к другому значению.
В общем смысле, это карта соотвествия. Значением (ключом) может выступать произвольное значение, в то время как значением должен являться экземпляр basis.event.Emitter
(на данный момент). Соотношение ключа и значения вычисляется один раз (при первом "запросе") и кешируется. При этом, в случае разрушения экземпляра значения связь удаляется из карты.
Класс достаточно давний, и тем не менее долгое время оставался "в тени". Изменения в последних версиях
basis.js
привели к тому, что класс оказался весьма полезным и востребованным. Предполагается его дальнейшее развитие (улучшение интерфейса и исправление различных "нестыковок", так как он устарел по отношению к другим решениям) и появление его потоков, решающих новые задачи.
Класс предоставляет методы:
get(key, autocreate)
– метод возвращает ассоцированное значение дляkey
; если такого значение нет, то возвращаетсяundefined
; если передан параметрautocreate
, то, в случае отсутсвия значения, вызывается методcreate
с переданными значениями, а результат его выполенения сохраняется как ассоциированное значение.create(key, config)
– метод вызывается, когда для заданного значения (параметраkey
) не найдено значения; значениеconfig
может использовать как дополнительный объект конфигурации (передается вget
какautocreate
), но по умолчанию не используется.resolve(value)
– метод пытается вернуть ассоцированное значение дляvalue
, а если его нет, то создать новое значение; в отличае отget
этот метод всегда возвращает значение, вне зависимости от того, существовало ли оно до этого.
Особое внимание стоит уделить методу resolve
. Так как это основной, который используется для получения значения, в виду того, что гарантирует получение значения. Его реализация по умолчанию выглядит так:
basis.data.KeyObjectMap.prototype.resolve = function(value){
return this.get(this.keyGetter(value), value);
}
Можно заметить, что в качестве "ключа" используется результат выполнения keyGetter
. По умолчанию, это свойство хранит функцию, которая возвращает значение без изменений (basis.fn.$self
). Таким образом переданное значение и является ключом (по умолчанию), а так же является кофигурацией.
Если метод create
не переопределен, то, в качестве значения, создается экземпляр basis.data.Object
. Класс значения по умолчанию можно задать через свойство itemClass
(по умолчанию basis.data.Object
). Конфигурация для создания зависит от типа (см "Разрешение ключевого значения").
По умолчанию, при разрушении карты, разрушаются и все его значения. Это исторически сложившееся поведение. С версии 1.3
этим поведением можно управлять через свойство autoDestroyMembers
(по умолчанию true
). Если выставить его в false
, то значения не будут разрушены при разрушении карты.
Так же нужно помнить, что при разрушении объектов являющимися значениями, они удаляются из карты. Это делает карты удобными для хнанения значений:
var DataObject = basis.require('basis.data').Object;
var KeyObjectMap = basis.require('basis.data').KeyObjectMap;
var map = new KeyObjectMap({
create: function(key){
return new DataObject({ data: { value: key } })
}
});
console.log(map.get(1));
// > undefined
console.log(map.resolve(1));
// > basis.data.Object { data: { value: 1 } }
console.log(map.get(1) === map.resolve(1));
// > true
map.resolve(1).destroy();
console.log(map.get(1));
// > undefined
В настоящий момент, разрушения значения, которое является ключом не ведет к удалению из карты. Это будет исправлено в будущих версиях
basis.js
.
Ключом может являться произвольное значение. Но в зависимости от типа значения, меняется способ его разрешения:
– если значение является экземпляром basis.data.Object
, то в качестве значения ключа используется свойство basisObjectId
;
– в ином случае используется строковое представление значения.
Если значение является экземпляром basis.data.Object
, то оно преобразуется в конфигурацию { delegate: value }
или в { data: { id: value, title: value } }
. Обычно это подходящие конфигурации для создания нового экземпляра класса basis.data.Object
, который является классом значения по умолчанию.
Такое поведение схоже с разрешением признака группы в
basis.dom.wrapper.GroupingNode
и подмножества вbasis.data.dataset.Split
иbasis.data.dataset.Cloud
.
Карты используются в качестве кеша (переиспользования значений) или для получения одних и тех же значений в разных частях кода в условиях конкуренции.
Нередко нужно сохранять значения, так как они могут нести информацию о состоянии. Обычно в качестве таких значений выступают экземпляры basis.ui.Node
и выгодно сохранять их экземпляры, вместо пересоздания. В этом случае, так же необходимо запретить разрушение дочерних узлов через свойство destroyDataSourceMember
, если используется dataSource
.
var Node = basis.require('basis.ui').Node;
var DataObject = basis.require('basis.data').Object;
var Dataset = basis.require('basis.data').Dataset;
var KeyObjectMap = basis.require('basis.data').KeyObjectMap;
var items = [
new DataObject(),
new DataObject(),
];
var dataset = new Dataset();
var map = new KeyObjectMap({
itemClass: Node.subclass({
init: function(){
console.log('create');
Node.prototype.init.call(this);
},
destroy: function(){
console.log('destroy');
Node.prototype.init.call(this);
}
})
});
var view = new Node({
destroyDataSourceMember: false,
dataSource: dataset,
childFactory: function(config){
return map.resolve(config.delegate);
}
});
dataset.set(items);
// > create
// > create
dataset.clear();
dataset.set(items);
dataset.add(new DataObject());
// > create
Иногда требуется, например, синхронизировать некоторый набор с selection
узла, который выводит некоторый полный набор. В этом случае не известно что будет раньше, создадутся дочерние узлы через dataSource
и childFactory
или же синхронизируется selection
. В любом случае, должны получиться одни и те же узлы (basis.ui.Node
):
var Node = basis.require('basis.ui').Node;
var DataObject = basis.require('basis.data').Object;
var Dataset = basis.require('basis.data').Dataset;
var KeyObjectMap = basis.require('basis.data').KeyObjectMap;
var items = basis.array.create(3, function(){
return new DataObject();
});
var dataset = new Dataset({
items: items
});
var model2node = new KeyObjectMap({
itemClass: Node.subclass({
init: function(){
console.log('create');
Node.prototype.init.call(this);
},
destroy: function(){
console.log('destroy');
Node.prototype.init.call(this);
}
})
});
var view = new Node({
destroyDataSourceMember: false,
dataSource: dataset,
selection: { multiple: true },
childFactory: function(config){
return model2node.resolve(config.delegate);
}
});
// > create
// > create
// > create
view.selection.set(items.slice(1, 2).map(function(item){
return model2node.resolve(item);
}));