documentationfor yFiles for HTML 2.6

Associating Data with Graph Elements

It is often necessary to connect the graph items with the data model, or business data, they actually represent. yFiles for HTML provides two options to store the data linked with the graph items:

Most conveniently, business data can be associated with a graph element using the tag property which is common for all model item types. These so-called user tags are described in the general chapter about the graph model.

An alternative way to bind data to model items or even the graph itself is provided by the IMapper<K,V> interface. IMapper<K,V>s are map-like implementations which bind a value of type V to a given key of type K, the key typically is a graph item, e.g. a node. IMapper<K,V>s are used to provide additional information for layout algorithms, see section Layout Data. They can also be used to store additional data which is not part of the actual business data.

The serialization and deserialization of IMapper<K,V>s as well as user tags to the GraphML file format is supported by the GraphML reading and writing support, see section Adding Custom Data for Serialization.

get(K key): V
Gets the value which is associated with the key. The value which is returned for non-set entries is not specified. Most implementations in yFiles for HTML return null. Mapper<K,V> optionally allows defining a defaultValue for non-set entries.
set(key: K, value: V): void
Sets the value for the given key. Overwrites existing values.

Note that some IMapper<K,V> implementations are read-only, i.e. calling set won’t have an effect.

The default implementation is Mapper<K,V> which is backed by a map. It is a read/write implementation of IMapper<K,V> and additionally allows to define a defaultValue for non-set entries.

The factory method IMapper<K, V>.fromConstant<K,V> creates an IMapper<K,V> implementation which returns the given instance for all keys. The factory method IMapper<K, V>.fromDelegate<K,V> returns an IMapper<K,V> implementation which delegates to the given Function to return values for keys. Note that both implementations are read-only.

IMapper<K,V> is a convertible type that is automatically converted from native JavaScript Map instances. See Automatic Type Conversion.

Creating mapper implementations
// creating the default implementation
const defaultMapper = new Mapper()

// creating a mapper providing the same instance for each key
const constantMapper = IMapper.fromConstant('Always the same.')

// creating a mapper using a delegate
// to return the first label's text
const delegateMapper = IMapper.fromDelegate((node) => (node.labels.size > 0 ? node.labels.get(0).text : null))
// creating the default implementation
const defaultMapper = new Mapper()

// creating a mapper providing the same instance for each key
const constantMapper = IMapper.fromConstant('Always the same.')

// creating a mapper using a delegate
// to return the first label's text
const delegateMapper = IMapper.fromDelegate((node: INode): string | null =>
  node.labels.size > 0 ? node.labels.get(0).text : null
)

The IMapperRegistry

Each IGraph has an IMapperRegistry which provides a means to associate IMapper<K,V>s with that graph. The registry can be obtained from IGraph’s mapperRegistry property. An IMapper<K,V> has to be added to the registry using a unique object, the tag. This object can be used to retrieve the mapper from the registry later.

Depending on the implementation, “unique” means there is no equal tag in the same registry or no tags with the same hash code. Although not mandatory, we recommend using different strings as tags.

Adding and retrieving mappers
// Using a constant for the tag makes it available all over the application
const mapperTag = 'my.application.MapperTag'

/**
 * Register the mapper with the tag
 * @param {!IGraph} graph
 */
function registerMapper(graph) {
  graph.mapperRegistry.addMapper(INode.$class, YString.$class, mapperTag, new Mapper())
}

/**
 * Retrieve the mapper with the tag and get the mapped value for the given item
 * @param {!IGraph} graph
 * @param {!INode} node
 * @returns {*}
 */
function getMappedValue(graph, node) {
  const mapper = graph.mapperRegistry.getMapper(mapperTag)
  return mapper.get(node)
}// Using a constant for the tag makes it available all over the application
const mapperTag = 'my.application.MapperTag'

/**
 * Register the mapper with the tag
 */
function registerMapper(graph: IGraph): void {
  graph.mapperRegistry.addMapper(INode.$class, YString.$class, mapperTag, new Mapper<INode, string>())
}

/**
 * Retrieve the mapper with the tag and get the mapped value for the given item
 */
function getMappedValue(graph: IGraph, node: INode): any {
  const mapper = graph.mapperRegistry.getMapper(mapperTag)
  return mapper!.get(node)
}

Besides adding existing IMapper<K,V> instances the IMapperRegistry provides factory methods to create IMapper<K,V> instances and add them immediately:

Adding existing mappers
addMapper<K,V>(keyType: Class<K>, valueType: Class<V>, tag: Object, mapper: IMapper<K, V>): void
Registers the given mapper with the given tag.
Creating and adding mappers
createMapper<K,V>(keyType: Class<K>, valueType: Class<V>, tag: Object): Mapper<K, V>
Creates a Mapper<K,V>, registers it with the given tag, and returns the newly created instance.
createConstantMapper<K,V>(keyType: Class<K>, valueType: Class<V>, tag: Object, constant: V): IMapper<K, V>
Creates an IMapper<K,V> which always returns the given value, registers it with the given tag, and returns the newly created instance.
createDelegateMapper<K,V>(keyType: Class<K>, valueType: Class<V>, tag: Object, getter: MapperDelegate<K, V>): IMapper<K, V>
Creates an IMapper<K,V> which delegates to the given Function to return a value, registers it with the given tag, and returns the newly created instance.
Retrieving a registered mapper
getMapper<K,V>(tag: Object): IMapper<K, V>
Returns the mapper which is registered under the given tag.

The following example summarizes all we have learned in this chapter:

Working with IMapper<K,V>s and the IMapperRegistry
// Using a constant for the tag makes it available all over the application
const myTag = 'my.application.MyTag'

// create and register a new mapper
// create some nodes and map values to them
/**
 * Create and register a new mapper.
 * Also, create some nodes and map values to them.
 * @param {!IGraph} graph
 */
function initializeGraph(graph) {
  // create a new mapper instance
  const mapper = new Mapper()
  // register it
  graph.mapperRegistry.addMapper(INode.$class, YString.$class, myTag, mapper)
  for (let i = 0; i < 10; i++) {
    const node = graph.createNode()
    // associate a value with a node
    mapper.set(node, 'Index: ' + i)
  }
}

/**
 * Read the mapped value of a given node
 * @param {!IGraph} graph
 * @param {!INode} node
 * @returns {*}
 */
function getNodeValue(graph, node) {
  // retrieve the mapper using the constant value as tag
  const mapper = graph.mapperRegistry.getMapper(myTag)
  // get the mapped value
  return mapper.get(node)
}

// removes a node
// unmaps the associated value before removing the node
/**
 * Removes a node.
 * Unmaps the associated value before removing the node.
 * @param {!IGraph} graph
 * @param {!INode} node
 */
function removeNode(graph, node) {
  // retrieve the mapper using the constant value as tag
  const mapper = graph.mapperRegistry.getMapper(myTag)
  // remove the mapping for the removed node - the delete method is only available
  // for the concrete Mapper type. It is not part of the interface.
  mapper.delete(node)
  // remove the node itself
  graph.remove(node)
}// Using a constant for the tag makes it available all over the application
const myTag = 'my.application.MyTag'

// create and register a new mapper
// create some nodes and map values to them
/**
 * Create and register a new mapper.
 * Also, create some nodes and map values to them.
 */
function initializeGraph(graph: IGraph): void {
  // create a new mapper instance
  const mapper = new Mapper<INode, string>()
  // register it
  graph.mapperRegistry.addMapper(INode.$class, YString.$class, myTag, mapper)
  for (let i = 0; i < 10; i++) {
    const node = graph.createNode()
    // associate a value with a node
    mapper.set(node, 'Index: ' + i)
  }
}

/**
 * Read the mapped value of a given node
 */
function getNodeValue(graph: IGraph, node: INode): any {
  // retrieve the mapper using the constant value as tag
  const mapper = graph.mapperRegistry.getMapper(myTag) as IMapper<INode, string>
  // get the mapped value
  return mapper.get(node)
}

// removes a node
// unmaps the associated value before removing the node
/**
 * Removes a node.
 * Unmaps the associated value before removing the node.
 */
function removeNode(graph: IGraph, node: INode): void {
  // retrieve the mapper using the constant value as tag
  const mapper = graph.mapperRegistry.getMapper(myTag) as Mapper<INode, string>
  // remove the mapping for the removed node - the delete method is only available
  // for the concrete Mapper type. It is not part of the interface.
  mapper.delete(node)
  // remove the node itself
  graph.remove(node)
}