1
1
# Data Layer Helper Library
2
2
This library provides the ability to process messages passed onto a dataLayer queue.
3
3
4
+ - [ Background] ( #what-is-a-datalayer-queue )
5
+ - [ Why Do We Need a Library?] ( #why-do-we-need-a-library )
6
+ - [ How Messages Are Merged] ( #how-messages-are-merged )
7
+ - [ Overwriting Existing Values] ( #overwriting-existing-values )
8
+ - [ Recursively Merging Values] ( #recursively-merging-values )
9
+ - [ Meta Commands] ( #meta-commands )
10
+ - [ Native Methods] ( #native-methods )
11
+ - [ Custom Methods] ( #custom-methods )
12
+ - [ Build and Test] ( #build-and-test )
13
+ - [ License] ( #license )
14
+
15
+
16
+ ## What is a dataLayer queue?
4
17
A dataLayer queue is simply a JavaScript array that lives on a webpage.
5
18
6
19
``` html
@@ -34,7 +47,7 @@ this data in a standard way, so page authors can avoid using a bunch of propriet
34
47
* It doesn't require page authors to expose the same data multiple times.
35
48
* It allows page authors to add, remove or change vendors easily.
36
49
37
- ## So why do we need a library?
50
+ ## Why do we need a library?
38
51
The dataLayer system make things very easy for page authors. The syntax is simple, and there are no
39
52
libraries to load. But this system does make life a little more difficult for vendors and tools
40
53
that want to consume the data. That's where this library comes in.
@@ -52,7 +65,7 @@ var helper = new DataLayerHelper(dataLayer);
52
65
```
53
66
54
67
This helper object will listen for new messages on the given dataLayer. Each new message will be
55
- merged into the helper's "abstract data model". This internal model object holds the most recent
68
+ merged into the helper's ** "abstract data model"** . This internal model object holds the most recent
56
69
value for all keys which have been set on messages processed by the helper.
57
70
58
71
You can retrieve values from the data model by using the helper's get() method:
@@ -80,7 +93,7 @@ Using the helper, you can retrieve the nested value using dot-notation:
80
93
helper .get (' one.two.three' ); // Returns 4.
81
94
helper .get (' one.two' ); // Returns {three: 4}.
82
95
```
83
- # How messages are merged
96
+ ## How Messages Are Merged
84
97
As each message is pushed onto the dataLayer, the abstract data model must be updated.
85
98
The helper library does this using a well-defined process.
86
99
@@ -118,17 +131,244 @@ Other | Array | Overwrite existing value
118
131
Other | Plain Object | Overwrite existing value
119
132
Other | Other | Overwrite existing value
120
133
134
+ ### Overwriting Existing Values
135
+ When the merging action is "Overwrite exsting value", the result of the operation is very
136
+ simple. The existing value will be completely discarded and the new value will take its
137
+ place in the abstract data model. The following table provides some examples:
138
+
139
+
140
+ Existing Value | New Value | Result of Overwrite
141
+ -----------------|------------------|---------------------
142
+ [ 1, 2, 3] | 'hello' | 'hello'
143
+ {ducks: 'quack'} | [ 1, 2, 3] | [ 1, 2, 3]
144
+ {ducks: 'quack'} | 'hello' | 'hello'
145
+ 'hello' | [ 1, 2, 3] | [ 1, 2, 3]
146
+ 'hello' | {ducks: 'quack'} | {ducks: 'quack'}
147
+ 'hello' | 42 | 42
148
+
149
+ ### Recursively Merging Values
150
+ When the merging action is "Recursively Merge", the result of the operation will be the
151
+ result of iterating through each property in the new value, and for each property, deciding
152
+ how to copy that sub-key/value into the abstract data model by looking at the type of that
153
+ sub-key in the existing value. If the key does not exist on the existing value, the new
154
+ value is simply assigned to the abstract model. The following examples demonstrate this:
155
+
156
+ Existing Value | New Value | Result of Overwrite
157
+ -------------------|-------------------------------|----------------------------
158
+ {one: 1, three: 3} | {two: 2} | {one: 1, three: 3, two: 2}
159
+ {one: 1, three: 3} | {three: 4} | {one: 1, three: 4}
160
+ {one: {two: 3}} | {one: {four: 5}} | {one: {two: 3, four: 5}}
161
+ {one: {two: 3}} | {two: 4} | {one: {two: 3}, two: 4}
162
+ [ ] | [ 'hello'] | [ 'hello']
163
+ [ 1] | [ undefined, 2] | [ 1, 2]
164
+ [ 1, {two: 3}] | [ undefined, {two: 4, six: 8}] | [ 1, {two: 4, six: 8}]
165
+
166
+ ### Meta Commands
167
+ Using the above methods alone, some operations on the abstract model are somewhat cumbersome.
168
+ For example, appending items onto the end of an existing array requires you to know the
169
+ length of the existing array and then requires you to clumsily build an array that can be
170
+ merged onto the existing value. To make these cases easier, we provide a set of alternative
171
+ syntaxes for updating values that are already in the abstract data model.
172
+
173
+ The first of these syntaxes allows you to call any method supported on the existing type.
174
+ For example, if the existing value in the abstract model is an Array, you'd have a wide
175
+ variety of APIs that can be called (e.g. push, pop, concat, shift, unshift, etc.). To invoke
176
+ this syntax, you would push a "command array" onto the dataLayer instead of a normal message
177
+ object.
121
178
179
+ ``` js
180
+ dataLayer .push ([' abc.push' , 4 , 5 , 6 ]);
181
+ ```
122
182
123
- TODO(bkuhn): More documentation coming here...
183
+ A command array is a normal JavaScript array, where the first element is a string. The string
184
+ contains the key of the value to update, followed by a dot (.), followed by the name of the
185
+ method to invoke on the value. In the above example, the key to update is 'abc', and the
186
+ method to invoke is the 'push' method. The string may be followed by zero or more arguments,
187
+ which will be passed to the invoked method.
188
+
189
+ If the given method name does not exist on the existing value, or if the invocation throws an
190
+ exception, the assignment will be ignored, and a warning message will be logged to the browser's
191
+ developer console (if available).
192
+
193
+ ### Native Methods
194
+ Browsers come with dozens, if not hundreds, of useful APIs. Any method supported by the
195
+ existing value can be called using the command array syntax. Here are some additional examples:
196
+
197
+ <table >
198
+ <tr >
199
+ <td><b>Existing Key:</b></td>
200
+ <td>abc</td>
201
+ </tr >
202
+ <tr >
203
+ <td><b>Existing Value:</b></td>
204
+ <td>[1, 2, 3]</td>
205
+ </tr >
206
+ <tr >
207
+ <td><b>Command Array:</b></td>
208
+ <td>dataLayer.push(['abc.push', 4, 5, 6])</td>
209
+ </tr >
210
+ <tr >
211
+ <td><b>Result:</b></td>
212
+ <td>[1, 2, 3, 4, 5, 6]</td>
213
+ </tr >
214
+ </table >
215
+
216
+ In the following example, no arguments are provided:
217
+
218
+ <table >
219
+ <tr >
220
+ <td><b>Existing Key:</b></td>
221
+ <td>abc</td>
222
+ </tr >
223
+ <tr >
224
+ <td><b>Existing Value:</b></td>
225
+ <td>[1, 2, 3]</td>
226
+ </tr >
227
+ <tr >
228
+ <td><b>Command Array:</b></td>
229
+ <td>dataLayer.push(['abc.pop'])</td>
230
+ </tr >
231
+ <tr >
232
+ <td><b>Result:</b></td>
233
+ <td>[1, 2]</td>
234
+ </tr >
235
+ </table >
236
+
237
+
238
+ In the following example, the value to update (bbb) is nested inside a top level object (aaa):
239
+
240
+ <table >
241
+ <tr >
242
+ <td><b>Existing Key:</b></td>
243
+ <td>aaa.bbb</td>
244
+ </tr >
245
+ <tr >
246
+ <td><b>Existing Value:</b></td>
247
+ <td>[1, 2, 3]</td>
248
+ </tr >
249
+ <tr >
250
+ <td><b>Command Array:</b></td>
251
+ <td>dataLayer.push(['aaa.bbb.push', 4])</td>
252
+ </tr >
253
+ <tr >
254
+ <td><b>Result:</b></td>
255
+ <td>[1, 2, 3, 4]</td>
256
+ </tr >
257
+ </table >
258
+
259
+ And the following example demonstrates an operation on a Date object. Remember that all types
260
+ are supported, not just Arrays:
261
+
262
+ <table >
263
+ <tr >
264
+ <td><b>Existing Key:</b></td>
265
+ <td>time</td>
266
+ </tr >
267
+ <tr >
268
+ <td><b>Existing Value:</b></td>
269
+ <td>Fri Dec 20 2013 15:23:22 GMT-0800 (PST)</td>
270
+ </tr >
271
+ <tr >
272
+ <td><b>Command Array:</b></td>
273
+ <td>dataLayer.push(['time.setYear', 2014])</td>
274
+ </tr >
275
+ <tr >
276
+ <td><b>Result:</b></td>
277
+ <td>Fri Dec 20 2014 15:23:22 GMT-0800 (PST)</td>
278
+ </tr >
279
+ </table >
280
+
281
+ Notice that because command arrays are processed asynchronously, nothing can be done with the
282
+ return values from these method invocations. This brings us to our second syntax for updating
283
+ values in the abstract data model.
284
+
285
+ ### Custom Methods
286
+ So far, we've seen that objects (messages) can be pushed onto the dataLayer, as well as arrays
287
+ (command arrays). Pushing a function onto the dataLayer will also allow you to update the abstract
288
+ data model, but with custom code. This technique has the added benefit of being able to handle
289
+ return values of any native method calls made from within the function.
290
+
291
+ When a function is processed, it will be executed in the context of the abstract data model. The
292
+ value of "this" will be an interface that represents the current abstract data model. This
293
+ interfact will provide two APIs: get(key) and set(key, value). The following examples demonstrate
294
+ how these APIs can be used to update values in the abstract data model.
295
+
296
+ <table >
297
+ <tr >
298
+ <td><b>Existing Key:</b></td>
299
+ <td>time</td>
300
+ </tr >
301
+ <tr >
302
+ <td><b>Existing Value:</b></td>
303
+ <td>Fri Dec 20 2013 15:23:22 GMT-0800 (PST)</td>
304
+ </tr >
305
+ <tr >
306
+ <td><b>Custom function:</b></td>
307
+ <td><pre>dataLayer.push(function() {
308
+ this.get('time').setMonth(0);
309
+ })</pre ></td >
310
+ </tr >
311
+ <tr >
312
+ <td><b>Result:</b></td>
313
+ <td>Fri Jan 20 2013 15:23:22 GMT-0800 (PST)</td>
314
+ </tr >
315
+ </table >
316
+
317
+ The following example demonstrates updating a nested value:
318
+
319
+ <table >
320
+ <tr >
321
+ <td><b>Existing Key:</b></td>
322
+ <td>aaa.bbb.ccc</td>
323
+ </tr >
324
+ <tr >
325
+ <td><b>Existing Value:</b></td>
326
+ <td>[1, 2, 3]</td>
327
+ </tr >
328
+ <tr >
329
+ <td><b>Custom function:</b></td>
330
+ <td><pre>dataLayer.push(function() {
331
+ var ccc = this.get('aaa.bbb.ccc');
332
+ ccc.push(ccc.pop() * 2);
333
+ })</pre ></td >
334
+ </tr >
335
+ <tr >
336
+ <td><b>Result:</b></td>
337
+ <td>[1, 2, 6]</td>
338
+ </tr >
339
+ </table >
340
+
341
+
342
+ The following example demonstrates overwriting a value:
343
+
344
+ <table >
345
+ <tr >
346
+ <td><b>Existing Key:</b></td>
347
+ <td>abc</td>
348
+ </tr >
349
+ <tr >
350
+ <td><b>Existing Value:</b></td>
351
+ <td>[1, 2, 3]</td>
352
+ </tr >
353
+ <tr >
354
+ <td><b>Custom function:</b></td>
355
+ <td><pre>dataLayer.push(function() {
356
+ this.set('abc', {xyz: this.get('abc')});
357
+ })</pre ></td >
358
+ </tr >
359
+ <tr >
360
+ <td><b>Result:</b></td>
361
+ <td>{xyz: [1, 2, 3]}</td>
362
+ </tr >
363
+ </table >
124
364
125
365
126
366
127
367
128
- For more background on what a "dataLayer" is, please see:
129
368
130
- 1 . [ Justin Cutroni's Original Blog Post] ( http://cutroni.com/blog/2012/05/14/make-analytics-better-with-tag-management-and-a-data-layer/ )
131
- 2 . [ Google Tag Manager Docs] ( https://developers.google.com/tag-manager/devguide#datalayer )
369
+ TODO(bkuhn): More documentation coming here...
370
+
371
+
132
372
133
373
134
374
## Build and Test
0 commit comments