diff --git a/README.md b/README.md index 999d940..135ba3a 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,29 @@ The value of the `loop` attribute is not a pure expressions evaluation, and it d So you don't need to declare all the available variables (in this case, the index is skipped), and the expressions after `in` doesn't need to be a local variable, it can be any expressions. +#### Loop meta + +Inside a loop, you have access to a special `loop` object, which contains information about the loop currently being executed: + +- `loop.index` - the current iteration of the loop (0 indexed) +- `loop.remaining` - number of iterations until the end (0 indexed) +- `loop.first` - boolean indicating if it's the first iteration +- `loop.last` - boolean indicating if it's the last iteration +- `loop.length` - total number of items + +Example: + +```html + +
  • Item value: {{ item }}
  • +
  • Current iteration of the loop: {{ loop.index }}
  • +
  • Number of iterations until the end: {{ loop.remaining }}
  • +
  • This {{ loop.first ? 'is' : 'is not' }} the first iteration
  • +
  • This {{ loop.last ? 'is' : 'is not' }} the last iteration
  • +
  • Total number of items: {{ loop.length }}
  • +
    +``` + ### Scopes You can replace locals inside certain area wrapped in a `` tag. For example you can use it after [posthtml-include](https://github.com/posthtml/posthtml-include) diff --git a/lib/index.js b/lib/index.js index 15c4eb7..ca42e15 100644 --- a/lib/index.js +++ b/lib/index.js @@ -55,6 +55,29 @@ function executeScope (scope, locals, node) { return walk({ locals: scope }, node.content) } +/** + * @description Returns an object containing loop metadata + * + * @method getLoopMeta + * + * @param {Integer|Object} index Current iteration + * @param {Object} target Object being iterated + * + * @return {Object} Object containing loop metadata + */ +function getLoopMeta (index, target) { + index = Array.isArray(target) ? index : Object.keys(target).indexOf(index) + const arr = Array.isArray(target) ? target : Object.keys(target) + + return { + index: index, + remaining: arr.length - index - 1, + first: arr.indexOf(arr[index]) === 0, + last: index + 1 == arr.length, + length: arr.length + } +} + /** * @author Jeff Escalante Denis (@jescalan), * Denis Malinochkin (mrmlnc), @@ -324,10 +347,12 @@ function walk (opts, nodes) { // run the loop, different types of loops for arrays and objects if (Array.isArray(target)) { for (let index = 0; index < target.length; index++) { + opts.locals.loop = getLoopMeta(index, target) m.push(executeLoop(keys, target[index], index, opts.locals, treeString)) } } else { for (let key in target) { + opts.locals.loop = getLoopMeta(key, target) m.push(executeLoop(keys, target[key], key, opts.locals, treeString)) } } diff --git a/test/expect/loop_metadata.html b/test/expect/loop_metadata.html new file mode 100644 index 0000000..7bcea1d --- /dev/null +++ b/test/expect/loop_metadata.html @@ -0,0 +1,24 @@ + diff --git a/test/expect/loop_nested_metadata.html b/test/expect/loop_nested_metadata.html new file mode 100644 index 0000000..025c2e8 --- /dev/null +++ b/test/expect/loop_nested_metadata.html @@ -0,0 +1,47 @@ + diff --git a/test/fixtures/loop_metadata.html b/test/fixtures/loop_metadata.html new file mode 100644 index 0000000..0ed8021 --- /dev/null +++ b/test/fixtures/loop_metadata.html @@ -0,0 +1,10 @@ + diff --git a/test/fixtures/loop_nested_metadata.html b/test/fixtures/loop_nested_metadata.html new file mode 100644 index 0000000..c4be33b --- /dev/null +++ b/test/fixtures/loop_nested_metadata.html @@ -0,0 +1,16 @@ + diff --git a/test/test-loops.js b/test/test-loops.js index 22c8b19..952ca0c 100644 --- a/test/test-loops.js +++ b/test/test-loops.js @@ -120,3 +120,15 @@ test('Loops - expression error', (t) => { t.is(err.message, 'Invalid or unexpected token') }) }) + +test('Loops - metadata', (t) => { + return process(t, 'loop_metadata', { + locals: { items: [1, 2, 3] } + }) +}) + +test('Loops - nested metadata', (t) => { + return process(t, 'loop_nested_metadata', { + locals: { items: { foo: [1, 2], bar: [3, 4] } } + }) +})