gulp-nunjucks-render and adding data from single file - gulp

How can I include data to gulp-nunjucks template from separate file?
//template/data/data.html
{% set
list = [
{
title: 'Item1'
},
{
title: 'Item2'
}
]
%}
This simple solution doesn't work.
{% include "data/json.html" %}

This should work if you use import instead of include, https://mozilla.github.io/nunjucks/templating.html#import
Try this (I used .njk extensions, but you can use .html, it won't matter):
//template/data/data.njk
{% set list = [
{
title: 'Item1'
},
{
title: 'Item2'
}] %}
And in the file where you want to use the {{ list }} variable:
//template/other-file.njk
{% from 'data/data.njk' import list %}
{{ list }}
Any top-level variables that are {% set %} or any macros that are defined are available via import.

Related

Pass an Array to an include to loop over

I have an include that can have >1 buttons depending on what is passed in.
Currently I have the following in the include:
{% if include.buttons %}
{% for button in include.buttons %}
<a class="{{ button.classes }}" href="{{ button.url }}">{{ button.title }}</a>
{% endfor %}
{% endif %}
Then I am trying to pass in the following data:
{% assign buttons = '[{ "title": "button 1", "url": "https://#", "classes": "btn btn-transparent" }, { "title": "button 2", "url": "https://#", "classes": "btn btn-primary" }]' %}
{% include header.html
buttons=buttons
%}
What I can't work out is how to pass the data correctly to the include so that I may loop through it.
The problem is the assignment of the data as a array. In liquid you can not directly initialize arrays. A workaround is to play with split.
However, using jekyll you can provide arrays via data files. Simply put your buttons in a file, say _data\buttons.yml with:
postXX:
- button1:
- title: "button 1"
- url: "https://#"
- classes: "btn btn-transparent"
- button2:
- title: "button 2"
- url: "https://#"
- classes: "btn btn-primary"
Now you could put a reference in the yaml-header of your post/page like:
---
your other yaml options....
buttons: postXX
---
Finally, assign the buttons and include them as you did in your code.
{% assign buttons = site.data.buttons[page.buttons] %}
{% include header.html
buttons=buttons
%}
With Liquid, you can't create an array with a literal expression like {% assign myArray = ["one","two","three"] %}.
You can only :
create empty one : {% assign emptyArray = "" | split: "" %}
create one from string : {% assign myArray = "one two three" | split: " " %}
You can then manipulate your array :
add an element to array : push or shift (jekyll specific filters)
remove an element from array : pop or unshift (jekyll specific filters)
merge two arrays with concat
and so on ...
So, your array can only come from a liquid array manipulation or some datas contained in configuration, data file or page front matter.

How to limit number of iterations when looping through object in nunjucks

I have a js object like this:
var data = [
{ src: "src1", name: "name 1" },
{ src: "src2", name: "name 2" },
{ src: "src3", name: "name 3" }
]
I am looping through it with Nunjucks:
{% for object in data %}
{{object.src}}
{% endfor %}
But I want to limit the number of iterations to 2.
How do I do that with Nunjucks?
I know there is a range option but I couldn't find how to use it in this case.
You could accomplish this a couple different ways:
A) Use loop.index0 special variable (Demo in Codepen)
Inside a for loop, you can use loop.index0 instead limit-var
{% for obj in data %}
{% if loop.index0 < 2 %}
{{obj.src}}: {{obj.name}}
{% endif %}
{% endfor %}
B) Add Custom Filter (Demo in Codepen)
But more readable is add custom filter limit and use it
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
var data = [
{src: "src1", name: "name 1"},
{src: "src2", name: "name 2"},
{src: "src3", name: "name 3"}
];
env.addFilter('limit', function(arr, limit) {
return arr.slice(0, limit);
});
var res = nunjucks.renderString(`
{% for obj in data | limit(2) %}
{{obj.src}}: {{obj.name}}
{% endfor %}`,
{data}
);
console.log(res);
C) Use native slice() function (Demo in Codepen)
{% for obj in data.slice(0, 2) %}
{{obj.src}}: {{obj.name}}
{% endfor %}

Multiple Nunjucks files with different JSON data using gulp

I would like to use gulp and Nunjucks to generate multiple template files at once with varying content. These templates will all have the exact same layout, but pass in different variables for text/images.
I am able to successfully generate a single index.html file, but am unsure how to set this up for multiple files to be created at once. Here is a simplified version of what I have:
gulpfile.js
var nunjucks = require('gulp-nunjucks-render');
var data = require('gulp-data');
gulp.task('nunjucks', function () {
return gulp
.src('./src/layouts/**/*.+(html|nunjucks)')
.pipe(data(function () { return require('./src/data.json'); }))
.pipe(nunjucks({ path: ['./src/templates'] }))
.pipe(gulp.dest('./dist'));
});
layout.nunjucks
<head>
{% block title %} {% endblock %}
</head>
index.nunjucks
{% extends "layout.nunjucks" %}
{% block title %}
<title>{{ title }}</title>
{% endblock %}
data.json
{
"title": "DEFAULT"
}
What is the best way to alter this workflow to generate multiple files where each has a different title?
I've found that I can create multiple index.nunjucks files, but it looks like they all use the same data.json file. I also wouldn't want to make a separate JSON file for each.
Could I add an array to my data.json file to have Nunjucks loop through and create a separate file for each object found? Such as the following:
data.json
{
"files": [{
"title": "DEFAULT",
}, {
"title": "DEFAULT #2"
}]
}
Or is there a way to set variables within the index.nunjucks file without relying on storing them in JSON?
Found the way to do this: in each Nunjucks index file, I set a variable named email to the filename and updated my data.json file with a new object matching the filename with its own content.
default.nunjucks
{% set email = default %}
{% include "index.nunjucks" %}
test.nunjucks
{% set email = test %}
{% include "index.nunjucks" %}
index.nunjucks
{% extends "layout.nunjucks" %}
{% block title %}
<title>{{ email.title }}</title>
{% endblock %}
layout.nunjucks
<head>
{% block title %} {% endblock %}
</head>
data.json
{
"default":
{
"title": "TITLE"
},
"test":
{
"title": "TEST TITLE"
}
}
When compiled through gulp, two separate emails are created with their own title using the same template.

Find object by id in an array of JSON objects with Twig

I am being passed an array that unfortunately I cannot restructure:
"options": [
{"name":"namea","text":"valuea"},
{"name":"nameb","text":"valueb"},
{"name":"namec","text":"valuec"},
{"name":"named","text":"valued"}
]
I need to be able to find the object with the name equal to namea, nameb, namec, etc. and then produce the appropriate text. I tried the following and few other variations of that but could not get it to work:
{% for item in event.options %}
{% if item.name == "nameb" %}
{{ item.text|capitalize }}
{% endif %}
{% endfor %}
Thanks in advance for any help.
Edit: I basically only have access to a text box to input HTML and Twig. I do not have access to framework, custom extensions, etc.
Edit: JSON with exact formatting:
{
"source": "TEST.COM",
"trigger": "test",
"options": [{
"name":"test_date",
"value":"31-05-2017"
},
{
"name":"test_number",
"value":"9081003"
},
{
"name":"test_test",
"value":"9asd003"
},
{
"name":"Name",
"value":"Todd"
},
{
"name":"test_other",
"value":"kslkjsfd"
},
{
"name":"test_help",
"value":"908sdf3"
}]
}
This is going to be very hacky either way (I feel the javascript way as mentioned in the comments would still be less hacky).
However as its multilined we could write a basic parser like thing (this is by no means bullet proof and fails if the content has a ", or the structure changes or whatever changes to be honest):
{# create an array of lines #}
{% set event = event|split('\n') %}
{# an array to hold our options #}
{% set options = [] %}
{# using a bool to track if we are inside the options part #}
{% set inOptions = false %}
{# this holds the name of the last name value we have passed #}
{% set currentKey = '' %}
{# go through each line #}
{% for line in event %}
{# if we are inside the options tag do our magic #}
{% if inOptions %}
{# check if the line starts with "name": and track the current key name #}
{# or check if it starts with "value": and set the current key to that value #}
{% if line matches '/"name":/' %}
{# split line on the double quotes and get the last bit #}
{% set currentKey = line|split('"') %}
{# cant get the array index piped in one go idk.. #}
{% set currentKey = currentKey[3] %}
{% elseif line matches '/"value":/i' %}
{% set currentValue = line|split('"') %}
{% set options = options|merge({(currentKey): currentValue[3]}) %}
{% elseif line matches '/\s*}]/' %}
{% set inOptions = false %}
{% endif %}
{% endif %}
{# check for the options sectioning start #}
{% if line matches '/^"options/' %}
{% set inOptions = true %}
{% endif %}
{% endfor %}
{{ dump(options) }}
This returns in my dump (which you didn't have so you cant see):
array:6 [▼
"test_date" => "31-05-2017"
"test_number" => "9081003"
"test_test" => "9asd003"
"Name" => "Todd"
"test_other" => "kslkjsfd"
"test_help" => "908sdf3"
]
As an actual array where you can then call options.test_date but all this is super hacky, it still can be improved but twig is not made for it and the syntax gets so clunky its hard to maintain.

Comparing variables in template to build JSON - Ansible

Starting off with Ansible and I am trying to use ReST API to interact with an external application.Maybe I am missing something simple here.
I am trying to compare every host in my inventory file with the POD name specified in the variable file used by the role that invokes the jinja2 template.
My inventory file looks like this:
[all]
'POD-9'
'POD-10'
Variable file :
pods:
params:
- name: POD-9
- name: POD-10
{% for pod in pods.params %}
{% if '{{ inventory_hostname }}' == '{{ pod.name }}' %}
<generate JSON template here>
{% endif %}
{% endfor %}
The if statement however does not take effect. I want the template to be generated only in the inventory_hostname is equal to the pod name in the variable file
The current JSON file includes both :
{
"pod": {
"name": "POD-9"
}
"pod": {
"name": "POD-10"
}
}
In Jinja2 the double curly braces are used as a print statement. If you access variables inside tags don’t put the braces around them
{% for pod in pods.params %}
{% if inventory_hostname == pod.name %}
<generate JSON template here>
{% endif %}
{% endfor %}
Found the problem :
{% if pod.name == inventory_hostname %}