The goal is to use the variables defined in the front-matter section in a particular page.
Here my structure of the file system:
_Components
c1.html
c2.html
Here I have defined the attributes in the front-matters.
_Includes > Components
c1.html
Here I want to use a loop to refer to a variable defined in the _Components > c1.html page.
How can I achieve this goal ?
In my _Includes > Components > c1.html I have the following code:
<body class="full">
{% assign fullcomponents = site.components %}
{% for component in fullcomponents | where:"title","c1html" %}
{% component.title %}
{% endfor %}
<div class="container-fluid" id="componentscontainer">
<div class="col-md-12">
<div class="panel panel-primary" id ="panelcomponentlarge">
<div class="panel-heading" >
Chart C3 Line
</div>
etc...
Surely I'm missing some trivial things.
SECOND ATTEMPT
I figured out that I can provide a data layer for that so, I tried to split this information into a new data file.
Here the content of components.json
{
"Components": [
"ChartC3Line":{
"component":"ChartC3Line",
"description":"This is an attempt"
},
"ChartC3Lines":{
"component":"ChartC3Lines",
"description":"This is an attempt"
}
]
}
And I'm trying to get this information with the following code:
{% assign comp = site.data.components.Components[ChartC3Line] %}
HTML:
{% highlight html linenos%}
<p> Description: {{ comp.description }} </p>
but anything is coming up.
THIRD ATTEMPT
I found a solution but I don't like it at all here my new json file
{
"ChartC3":[
{
"component":"ChartC3Line",
"description":"This is an attempt"
}],
"ChartC4":[
{
"component":"ChartC3Line",
"description":"This is an attemptSSSSSSS"
}]
}
I don't want to have an object of several arrays of one element!
Here the code to retrieve the right information:
{% assign comp = site.data.components.ChartC4[0] %}
HTML:
{% highlight html linenos%}
<p> Description: {{ comp.description }} </p>
SOLVED
Following the structure of a json file, I changed my structure in an easier way:
{
"ChartC3":
{
"component":"ChartC3Line",
"description":"This is an attempt"
},
"ChartC4":
{
"component":"ChartC3Line",
"description":"This is an attemptSSSSSSS"
}
}
Now I can easily have access to the right object.
{% assign comp = site.data.components.ChartC3 %}
HTML:
{% highlight html linenos%}
<p> Description: {{ comp.description }} </p>
Related
I have a Hugo SSG website.
My JSON file is saved in: /data/source.json
I created a template file: /layouts/test.html that has this content:
<!-- Data is in /data/source.json -->
{{ range .Site.Data.source }}
{{ range .names }}
<!-- person_name -->
<p>{{ .identifier.person_name }}</p>
{{ end }}
{{ end }}
How do I render this .html content to the website?
The Hugo documentation here doesn't explain what I need to do after making the .html file.
It looks like you have two range operations when you need only one. You are not ranging over multiple source.json files, correct?
Assuming you have a file data/source.json like this:
{
"names": [
{
"identifier": {
"person_name": "Joe Wilson"
}
},
{
"identifier": {
"person_name": "Nancy Wilson"
}
}
]
}
Then it should be possible to render those names like this:
{{ range .Site.Data.source.names }}
<p>{{ .identifier.person_name }}</p>
{{ end }}
Add that to a layout in your themes/{theme-name}/layouts and it will render on the pages using that template.
I have added custom text field inside layout header using json :
{
"type": "text",
"id": "my_text",
"label": "MyText"
}
Then I have called it inside luquid file like this:
{% if settings.my_text%}
<li class="test">{{ 'layout.header.my_text' }}</li>
{% endif %}
In return I'm getting same (layout.header.my_text ) not the value which I added from customizer
So the solution is I changed : layout.header.my_text to settings.my_text .
I want to create a list of modal popups with a for-loop, each of them displaying different text.
The site is created with Jekyll with the Liquid templating engine.
In particular, I want to create the list of my scientific publications, for each of them with 2 icons: one for the bibtex entry and one for the abstract. This information is stored in a yaml file.
I m following this simple tutorial for modal popups.
The popups work, but the text is the same for all the entries. How is possible to generate independent modal popups?
This is the html
{% for papers in papers %}
{% for content in paper.papers %}
<a title="{{content.name}}"><i class='{{content.icon}}' data-modal-target="#modal"></i></a>
<div class="modal" id="modal">
<div class="modal-header">
<div class="title">{{content.name}}</div>
<button data-close-button class="close-button">×</button>
</div>
<!-- text to display -->
<div class="modal-body">{{content.text}}</div>
</div>
{% endfor %}
{% endfor %}
and this is the Javascript code:
const openModalIcons = document.querySelectorAll('[data-modal-target]')
const closeModalButtons = document.querySelectorAll('[data-close-button]')
const overlay = document.getElementById('overlay')
openModalIcons.forEach(icon => {
icon.addEventListener('click', () => {
const modal = document.querySelector(icon.dataset.modalTarget)
openModal(modal)
})
})
function openModal(modal) {
if (modal == null) return
modal.classList.add('active')
overlay.classList.add('active')
}
closeModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = button.closest('.modal')
closeModal(modal)
})
})
function closeModal(modal) {
if (modal == null) return
modal.classList.remove('active')
overlay.classList.remove('active')
}
overlay.addEventListener('click', () => {
const modals = document.querySelectorAll('.modal.active')
modals.forEach(modal => {
closeModal(modal)
})
})
The problem is that all your modals have id="modal", so the selector #modal is probably always selecting the first one. The id attribute should be unique in the entire document.
Something like this should work:
{% for papers in papers %}
{% for content in paper.papers %}
<a title="{{content.name}}"><i class='{{content.icon}}' data-modal-target="#paperModal{{forloop.parentloop.counter}}_{{forloop.counter}}"></i></a>
<div class="modal" id="paperModal{{forloop.parentloop.counter}}_{{forloop.counter}}">
<div class="modal-header">
<div class="title">{{content.name}}</div>
<button data-close-button class="close-button">×</button>
</div>
<!-- text to display -->
<div class="modal-body">{{content.text}}</div>
</div>
{% endfor %}
{% endfor %}
Instead of the for loop counter, you could also use the paper name as the ID (as long as it's unique), e.g. id="paperModal_{{content.name | slugify}}".
Edit: Edited the snippet to use forloop.counter and account for the nested for loop.
I have container.twig including component.twig and passing an object called 'mock'.
In container.twig:
{% set mock = {
title : "This is my title"
}
%}
{% include 'component.twig' with mock %}
This is working fine but I want to move the mock data to its own file. This isnt working:
Container.twig
{% include 'component.twig' with 'mock.twig' %}
In mock.twig
{% set mock = {
title : "This is my title"
}
%}
Im using gulp-twig but it works like standard twig in most respects. https://github.com/zimmen/gulp-twig
The problem
Twig context is never stored in the template object, so this will be very difficult to find a clean way to achieve this. For example, the following Twig code:
{% set test = 'Hello, world' %}
Will compile to:
<?php
class __TwigTemplate_20df0122e7c88760565e671dea7b7d68c33516f833acc39288f926e234b08380 extends Twig_Template
{
/* ... */
protected function doDisplay(array $context, array $blocks = array())
{
// line 1
$context["test"] = "Hello, world";
}
/* ... */
}
As you can see, the inherited context is not passed to the doDisplay method by reference, and is never stored in the object itself (like $this->context = $context). This deisgn allow templates to be reusable, and is memory-friendly.
Solution 1 : using global variables
I don't know if you are aware of Global Variables in Twig. You can do a bunch of hacks with them.
The easiest usage is to load all your globals inside your twig environment.
$loader = new Twig_Loader_Filesystem(__DIR__.'/view');
$env = new Twig_Environment($loader);
$env->addGlobal('foo', 'bar');
$env->addGlobal('Hello', 'world!');
Then, you can use {{ foo }} and {{ Hello }} in your whole application.
But there are 2 problems here:
As you're trying to load variables from twig files, I assume you have lots of variables to initialize depending on your feature and don't want to load everything all time.
you are loading variables from PHP scripts and not from Twig, and your question want to import variables from a twig file.
Solution 2 : using a Twig extension
You can also create a storage extension that provide a save function to persist some template's context somewhere, and a restore function to merge this stored context in another one.
proof_of_concept.php
<?php
require __DIR__.'/vendor/autoload.php';
class StorageTwigExtension extends Twig_Extension
{
protected $storage = [];
public function getFunctions() {
return [
new Twig_SimpleFunction('save', [$this, 'save'], ['needs_context' => true]),
new Twig_SimpleFunction('restore', [$this, 'restore'], ['needs_context' => true]),
];
}
public function save($context, $name) {
$this->storage = array_merge($this->storage, $context);
}
public function restore(&$context, $name) {
$context = array_merge($context, $this->storage);
}
public function getName() {
return 'storage';
}
}
/* usage example */
$loader = new Twig_Loader_Filesystem(__DIR__.'/view');
$env = new Twig_Environment($loader);
$env->addExtension(new StorageTwigExtension());
echo $env->render('test.twig'), PHP_EOL;
twig/variables.twig
{% set foo = 'bar' %}
{% set Hello = 'world!' %}
{{ save('test') }}
twig/test.twig
{% include 'variables.twig' %}
{{ restore('test') }}
{{ foo }}
Note: if you only want to import variables without actually rendering what's inside twig/variables.twig, you can also use:
{% set tmp = include('variables.twig') %}
{{ restore('test') }}
{{ foo }}
Final note
I'm not used to the JavaScript twig port, but it looks like you can still extend it, that's your go :)
Because of Twig's scoping rules (which I assume are replicated by the gulp version), you cannot pass variables up from a child template without creating a helper function. The closest thing you can do is to use inheritance to replicate this.
As such, your mock.twig file would become
{% set mock = {
title : "This is my title"
}
%}
{% block content %}{% endblock %}
Your container.twig would then become
{% extends 'mock.twig' %}
{% block content %}
{% include 'component.twig' with mock %}
{% endblock %}
This achieves your goals of separating the mock content from the templates for the most part and, using dynamic extends, you can do something like
{% extends usemock == 'true'
? 'contentdumper.twig'
: 'mock.twig' %}
with a contentdumper.twig file that is just a stub like so
{% block content %}{% endblock %}
and then set the usemock variable to determine if you will be using the mock data or not.
Hopefully this helps, even though it doesn't really solve the exact problem you are having.
Let you assume that I have this json file:
{
"components":[
{
"id" : "brandbar",
"name" : "brandbar.html",
"type":"navbar",
"description":"Bar with the brand",
"status": "draft"
},
{
"id":"topbar",
"type":"navbar",
"component":"Best brand",
"description":"Chart with Date"
}]
}
Now, I would like to have a loop on this json to retrieve the filenames.
{% for entry in site.data.component.components %}
<div class="col-md-12">
{% include {{ entry.name }} %}
</div>
{% endfor %}
The problem is that I can't use {{ entry.name }} in my include statement.
is there a way to achieve this goal ?
I don't want to loop directly in my folder... I would like to have a main file where I will fill in every important attribute about the components.
You can try to indent you json with spaces instead of tabs.
This might do the trick :
{"components":[
{
"id" : "brandbar",
"name" : "brandbar.html",
"type":"navbar",
"description":"Bar with the brand",
"status": "draft"
},
{
"id":"topbar",
"type":"navbar",
"component":"Best brand",
"description":"Chart with Date"
}
]}
Except that topbar has no name propriety, and this can cause an error.