Skip to content

Commit 40884d7

Browse files
committed
Add docs on merging, commands and custom methods.
1 parent 5aaf8e1 commit 40884d7

File tree

1 file changed

+247
-7
lines changed

1 file changed

+247
-7
lines changed

README.md

Lines changed: 247 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
# Data Layer Helper Library
22
This library provides the ability to process messages passed onto a dataLayer queue.
33

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?
417
A dataLayer queue is simply a JavaScript array that lives on a webpage.
518

619
```html
@@ -34,7 +47,7 @@ this data in a standard way, so page authors can avoid using a bunch of propriet
3447
* It doesn't require page authors to expose the same data multiple times.
3548
* It allows page authors to add, remove or change vendors easily.
3649

37-
## So why do we need a library?
50+
## Why do we need a library?
3851
The dataLayer system make things very easy for page authors. The syntax is simple, and there are no
3952
libraries to load. But this system does make life a little more difficult for vendors and tools
4053
that want to consume the data. That's where this library comes in.
@@ -52,7 +65,7 @@ var helper = new DataLayerHelper(dataLayer);
5265
```
5366

5467
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
5669
value for all keys which have been set on messages processed by the helper.
5770

5871
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:
8093
helper.get('one.two.three'); // Returns 4.
8194
helper.get('one.two'); // Returns {three: 4}.
8295
```
83-
# How messages are merged
96+
## How Messages Are Merged
8497
As each message is pushed onto the dataLayer, the abstract data model must be updated.
8598
The helper library does this using a well-defined process.
8699

@@ -118,17 +131,244 @@ Other | Array | Overwrite existing value
118131
Other | Plain Object | Overwrite existing value
119132
Other | Other | Overwrite existing value
120133

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.
121178

179+
```js
180+
dataLayer.push(['abc.push', 4, 5, 6]);
181+
```
122182

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>
124364

125365

126366

127367

128-
For more background on what a "dataLayer" is, please see:
129368

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+
132372

133373

134374
## Build and Test

0 commit comments

Comments
 (0)