|
| 1 | +--- |
| 2 | +title: LiveMap |
| 3 | +meta_description: "Create, update and receive updates for a key/value data structure that synchronizes state across clients in realtime." |
| 4 | +product: liveobjects |
| 5 | +languages: |
| 6 | + - javascript |
| 7 | +--- |
| 8 | + |
| 9 | +@LiveMap@ is a key/value data structure that synchronizes its state across users in realtime. It allows you to store primitive values, such as numbers, strings, booleans and buffers, as well as other Live Objects, "enabling you to build complex, hierarchical state structures":#composability. |
| 10 | + |
| 11 | +Conflicts in a LiveMap are automatically resolved following the Last Write Wins (LWW) semantics. The latest received operation on a key in a map will be applied, persisted in the LiveObjects state on a channel, and broadcast to all clients. |
| 12 | + |
| 13 | +h2(#create). Create LiveMap |
| 14 | + |
| 15 | +A @LiveMap@ instance can be created using the @channel.objects.createMap()@ method. It must be stored inside another @LiveMap@, such as the "root object":/docs/liveobjects/setup#root, to persist it within the LiveObjects state and share it with other clients. |
| 16 | + |
| 17 | +blang[javascript]. |
| 18 | + @channel.objects.createMap()@ is asynchronous, as the client sends the create operation to the Ably system and waits for an acknowledgment of the successful map creation. |
| 19 | + |
| 20 | +blang[javascript]. |
| 21 | + |
| 22 | + ```[javascript] |
| 23 | + const map = await channel.objects.createMap(); |
| 24 | + await root.set('myMap', map); |
| 25 | + ``` |
| 26 | + |
| 27 | +Optionally, you can specify an initial key/value structure when creating the map: |
| 28 | + |
| 29 | +blang[javascript]. |
| 30 | + |
| 31 | + ```[javascript] |
| 32 | + // Pass a regular JavaScript object reflecting the initial state |
| 33 | + const map = await channel.objects.createMap({ foo: 'bar', baz: 42 }); |
| 34 | + // You can also pass other Live Objects as values for keys |
| 35 | + await channel.objects.createMap({ nestedMap: map }); |
| 36 | + ``` |
| 37 | + |
| 38 | +h2(#value). Get value for a key |
| 39 | + |
| 40 | +Get the current value for a key in a map using the @LiveMap.get()@ method: |
| 41 | + |
| 42 | +blang[javascript]. |
| 43 | + |
| 44 | + ```[javascript] |
| 45 | + console.log('Value for my-key:', map.get('my-key')); |
| 46 | + ``` |
| 47 | + |
| 48 | +h2(#subscribe-data). Subscribe to data updates |
| 49 | + |
| 50 | +You can subscribe to data updates on a map to receive realtime changes made by you or other clients. |
| 51 | + |
| 52 | +<aside data-type='note'> |
| 53 | +<p> |
| 54 | + @LiveMap@ mutation methods do not directly modify the local map state. Instead, they send the intended operation to the Ably system, and the change is applied only when the corresponding realtime operation is echoed back to the client. This means that the state retrieved immediately after a mutation may not reflect the latest updates yet. You will be notified via subscription when the map is updated. |
| 55 | +</p> |
| 56 | +</aside> |
| 57 | + |
| 58 | +Subscribe to data updates on a map using the @LiveMap.subscribe()@ method: |
| 59 | + |
| 60 | +blang[javascript]. |
| 61 | + |
| 62 | + ```[javascript] |
| 63 | + map.subscribe((update) => { |
| 64 | + console.log('Map updated:', [...map.entries()]); |
| 65 | + console.log('Update details:', update); |
| 66 | + }); |
| 67 | + ``` |
| 68 | + |
| 69 | +The update object provides details about the change, listing the keys that were changed and indicating whether they were updated (value changed) or removed from the map. |
| 70 | + |
| 71 | +Example structure of an update object when the key **foo** is updated and the key **bar** is removed: |
| 72 | + |
| 73 | +```[json] |
| 74 | +{ |
| 75 | + { |
| 76 | + "foo": "updated", |
| 77 | + "bar": "removed" |
| 78 | + } |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +h2(#unsubscribe-data). Unsubscribe from data updates |
| 83 | + |
| 84 | +Use the @unsubscribe()@ function returned in the @subscribe()@ response to remove a map update listener: |
| 85 | + |
| 86 | +blang[javascript]. |
| 87 | + |
| 88 | + ```[javascript] |
| 89 | + // Initial subscription |
| 90 | + const { unsubscribe } = map.subscribe(() => console.log('Map updated')); |
| 91 | + // To remove the listener |
| 92 | + unsubscribe(); |
| 93 | + ``` |
| 94 | + |
| 95 | +Use the @LiveMap.unsubscribe()@ method to deregister a provided listener: |
| 96 | + |
| 97 | +blang[javascript]. |
| 98 | + |
| 99 | + ```[javascript] |
| 100 | + // Initial subscription |
| 101 | + const listener = () => console.log('Map updated'); |
| 102 | + map.subscribe(listener); |
| 103 | + // To remove the listener |
| 104 | + map.unsubscribe(listener); |
| 105 | + ``` |
| 106 | + |
| 107 | +Use the @LiveMap.unsubscribeAll()@ method to deregister all map update listeners: |
| 108 | + |
| 109 | +blang[javascript]. |
| 110 | + |
| 111 | + ```[javascript] |
| 112 | + map.unsubscribeAll(); |
| 113 | + ``` |
| 114 | + |
| 115 | +h2(#set). Set keys in a LiveMap |
| 116 | + |
| 117 | +Set a value for a key in a map by calling @LiveMap.set()@. This operation is synchronized across all clients and triggers data subscription callbacks for the map, including on the client making the request. |
| 118 | + |
| 119 | +Keys in a map can contain numbers, strings, booleans and buffers, as well as other LiveMaps and LiveCounters. |
| 120 | + |
| 121 | +blang[javascript]. |
| 122 | + This operation is asynchronous, as the client sends the corresponding update operation to the Ably system and waits for acknowledgment of the successful map key update. |
| 123 | + |
| 124 | +blang[javascript]. |
| 125 | + |
| 126 | + ```[javascript] |
| 127 | + await map.set('foo', 'bar'); |
| 128 | + await map.set('baz', 42); |
| 129 | + |
| 130 | + // Can also set a reference to another Live Object |
| 131 | + const counter = await channel.objects.createCounter(); |
| 132 | + await map.set('counter', counter); |
| 133 | + ``` |
| 134 | + |
| 135 | +h2(#remove). Remove a key from a LiveMap |
| 136 | + |
| 137 | +Remove a key from a map by calling @LiveMap.remove()@. This operation is synchronized across all clients and triggers data subscription callbacks for the map, including on the client making the request. |
| 138 | + |
| 139 | +blang[javascript]. |
| 140 | + This operation is asynchronous, as the client sends the corresponding remove operation to the Ably system and waits for acknowledgment of the successful map key removal. |
| 141 | + |
| 142 | +blang[javascript]. |
| 143 | + |
| 144 | + ```[javascript] |
| 145 | + await map.remove('foo'); |
| 146 | + ``` |
| 147 | + |
| 148 | +h2(#subscribe-lifecycle). Subscribe to Lifecycle Events |
| 149 | + |
| 150 | +Subscribe to lifecycle events on a map using the @LiveMap.on()@ method: |
| 151 | + |
| 152 | +blang[javascript]. |
| 153 | + |
| 154 | + ```[javascript] |
| 155 | + map.on('deleted', () => { |
| 156 | + console.log('Map has been deleted'); |
| 157 | + }); |
| 158 | + ``` |
| 159 | + |
| 160 | +Read more about "Live Objects lifecycle events":/docs/liveobjects/lifecycle#objects. |
| 161 | + |
| 162 | +h2(#unsubscribe-lifecycle). Unsubscribe from Lifecycle Events |
| 163 | + |
| 164 | +Use the @off()@ function returned in the @on()@ response to remove a lifecycle event listener: |
| 165 | + |
| 166 | +blang[javascript]. |
| 167 | + |
| 168 | + ```[javascript] |
| 169 | + // Initial subscription |
| 170 | + const { off } = map.on(('deleted') => console.log('Map deleted')); |
| 171 | + // To remove the listener |
| 172 | + off(); |
| 173 | + ``` |
| 174 | + |
| 175 | +Use the @LiveMap.off()@ method to deregister a provided lifecycle event listener: |
| 176 | + |
| 177 | +blang[javascript]. |
| 178 | + |
| 179 | + ```[javascript] |
| 180 | + // Initial subscription |
| 181 | + const listener = () => console.log('Map deleted'); |
| 182 | + map.on('deleted', listener); |
| 183 | + // To remove the listener |
| 184 | + map.off('deleted', listener); |
| 185 | + ``` |
| 186 | + |
| 187 | +Use the @LiveMap.offAll()@ method to deregister all lifecycle event listeners: |
| 188 | + |
| 189 | +blang[javascript]. |
| 190 | + |
| 191 | + ```[javascript] |
| 192 | + map.offAll(); |
| 193 | + ``` |
| 194 | + |
| 195 | +h2(#composability). Composability |
| 196 | + |
| 197 | +@LiveMap@ can store references to other LiveMaps or LiveCounters as values for its keys, allowing you to build complex, hierarchical state structures. This enables you to represent complex data models in your applications while ensuring realtime synchronization across clients. |
| 198 | + |
| 199 | +blang[javascript]. |
| 200 | + |
| 201 | + ```[javascript] |
| 202 | + // Create a hierarchy of objects using LiveMaps |
| 203 | + const counter = await channel.objects.createCounter(); |
| 204 | + const map = await channel.objects.createMap({ nestedCounter: counter }); |
| 205 | + const outerMap = await channel.objects.createMap({ nestedMap: map }); |
| 206 | + await root.set('outerMap', outerMap); |
| 207 | + |
| 208 | + // resulting structure: |
| 209 | + // root (LiveMap) |
| 210 | + // └── outerMap (LiveMap) |
| 211 | + // └── nestedMap (LiveMap) |
| 212 | + // └── nestedCounter (LiveCounter) |
| 213 | + ``` |
| 214 | + |
0 commit comments