Nunjucks dynamic page template - javascript

i'm using nunjucks (gulp) as templating language and i want to build a dynamic page template.
This is my Json:
"pages": [
{
uname: "Welcome",
title: "Page 1 Headline"
},
{
uname: "About",
title: "Page 2 Headline"
}
]
Currently i have a static page (html) template for each page:
{% extends "layout.html" %}
{% set active_page = "Welcome" %} //<- This needs to be dynamicly
{% block content %}
<h1>{{ page[0].title }}</h1> //<- This needs to be dynamicly
My first thought was to read the url parameters but i couldn't solve it in this way.
Any suggestions?

The solution was simple!
Need to do this:
index.njk
<!-- set title !!! -->
{% set title = 'home page' %} <!-- set title !!! -->
{% extends "layout.njk" %}
{% block header %} {% include "parts/header.njk" %} {% endblock %}
{% block main %}
<main class="main">
<!-- content -->
</main>
{% endblock %}
{% block footer %} {% include "parts/footer.njk" %} {% endblock %}
page1.njk
<!-- set title !!! -->
{% set title = 'page1' %}
{% extends "layout.njk" %}
{% block header %} {% include "parts/header.njk" %} {% endblock %}
{% block main %}
<main class="main">
<!-- content -->
</main>
{% endblock %}
{% block footer %} {% include "parts/footer.njk" %} {% endblock %}
layout.njk
<!-- layout.njk -->
<html lang="en">
<head>
<!-- here is the compiled title -->
<title>{{ title }}</title>
<link rel="stylesheet" href="styles/style.css">
</head>
<body class="page">
{% block header %}{% endblock %}
{% block main %}{% endblock %}
{% block footer %}{% endblock %}
</body>
</html>

If you are hoping to pass the data from data.json file
first you need to use someway to specify the page name in the data file itself.
Then you have to set the page name as a variable in the nunjucks page.
Now you can use this variable name to get the data relevant to the
page you are working in.
data.json
{
"pages": {
"welcome": {
"uname": "Welcome",
"title": "Page 1 Headline"
},
"about": {
"uname": "About",
"title": "Page 2 Headline"
},
}
}
index.njk
{% set pageName = 'welcome' %}
{% extends "layout.html" %}
{% set active_page = "Welcome" %}
{% block content %}
<h1>{{ page[pageName].title }}</h1>

{% macro inc(file, folder) %}
{% if folder %}
{% set folderPATH = folder %}
{% else %}
{% set folderPATH = file %}
{% endif %}
{% set path = folderPATH + "/" + file + ".njk" %}
{% include path %}
{% endmacro %}
{{ inc("menu") }} // {% include "menu/menu.njk" %}
{{ inc("content") }} // {% include "content/content.njk" %}

Related

Error: Liquid syntax error (/github/workspace/_includes/foot.html line 1): Expected end_of_string but found id included

I want to deploy in Github pages some code, but I get this error and I don't understand where the mistake is. The error is:
Error: Liquid syntax error (/github/workspace/_includes/foot.html line 1): Expected end_of_string but found id included
And the page:
`<!-- Optional JS for Bootstrap: jQuery first, then Bootstrap bundle JS -->
<script src="{{ '/js/jquery-3.5.1.min.js' | prepend: site.digital-assets }}"></script>
<script src="{{ '/js/bootstrap.bundle.min.js' | prepend: site.digital-assets }}"></script>
<!-- load other js -->
<script src="{{ '/js/lazysizes.min.js' | prepend: site.digital-assets }}" async></script>
{% if layout.gallery == true or page.gallery == true %}{% include js/gallery-js.html %}{% endif %}
{% if page.layout == "browse" %}{% include js/browse-js.html %}{% endif %}
{% if page.layout == "data" %}{% include js/table-js.html %}{% endif %}
{% if page.layout == "subjects" and site.data.theme.subjects-fields %}{% include js/cloud-js.html fields=site.data.theme.subjects-fields min=site.data.theme.subjects-min stopwords=site.data.theme.subjects-stopwords %}{% endif %}
{% if page.layout == "locations" and site.data.theme.locations-fields %}{% include js/cloud-js.html fields=site.data.theme.locations-fields min=site.data.theme.locations-min stopwords=site.data.theme.locations-stopwords %}{% endif %}
{% if page.layout == "timeline" %}{% include js/timeline-js.html %}{% endif %}
{% if page.layout == "map" %}{% include js/map-js.html %}{% endif %}
{% if page.layout == "item" %}{% include js/arrow-page-control-js.html %}{% endif %}
{% if page.layout == "search" %}{% include js/lunr-js.html %}{% endif %}
{%- if page.layout == "home-infographic" %}{% include js/carousel-js.html %}{% endif -%}`
If I delete the whole page the build works, but of course I need javascript.
I also tried deleting specific pieces of code but nothing.

Link element tags are disabled when a background image is set

I've got this base.html from which other html documents extend
base.html
<!DOCTYPE html>
<html>
<head>
<--!some css links and jquery source headers-->
<style>
{% block style %}
.container{
position: relative;
}
{% endblock style %}
</style>
</head>
<body>
<div class="container">
{% block breadcrumb %}{% endblock breadcrumb %}
{% block content %}{% endblock content %}
</div>
</body>
<script type="text/JavaScript">
{% block script %}
var imgName = "{% url 'img_file.png' %}";
document.body.style.backgroundImage = "url('"+ imgName.src +"')";
{% endblock script %}
</script>
<\html>
a document that extends from base.html
index.html
{% extends 'base.html' %}
{% block breadcrumb %}
<div class="nav nav-bar nav-bar-dark>
some link
</div>
{% endblock %}
{% block content %}
<form action="view-url" method="post">
{{ form }}
<input type="submit" class="btn" value"Submit">
</form>
{% endblock %}
the submit input works well but the link (to some url) seems to be disabled. When I remove the background image from the base.html, the link works just fine. Any help on what I'm missing here please ,

Call a javascript function of base template from another template in django

in my base.html template, I write a function. Can I call it from another template?
I tried like this. It doesn't work.
base.html:
<!-- ...code... -->
<script>
function registration(){
if(document.getElementById("registration").className==='hide'){
document.getElementById("registration").className='show'
}else{
document.getElementById("registration").className='hide'
}
}
</script>
another template
{% extends 'base.html' %}
{% block body %}
<script>
//if i re write the function here, it works
registration()
</script>
{% endblock body %}
You can try like this in your base.html
<html>
<head>
<!---Here all the CSS links --->
{% block css %}
{% endblock css %}
<head>
<body>
{% block body %}
{% endblock body %}
</body>
{% block script %}
//write your script here
{% endblock script %}
</html>
in other template you have to just do like this
for example about.html
{% block body %}
{% endblock body %}

Data from Twig Variable in _entry.twig to Javascript Function in External Script

I have created a _layout.twig file which acts as my base to hold content on the page -> _layout.twig:
<!DOCTYPE html>
<html lang="{{ craft.app.language }}">
<head>
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<meta charset="utf-8" />
<title>
{{ siteName }}
</title>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
name="viewport" />
<link rel="stylesheet"
href="https://unpkg.com/leaflet#1.4.0/dist/leaflet.css"
integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA=="
crossorigin="" />
{% includeCssFile siteUrl ~ 'assets/css/style.css' %}
</head>
<body>
{% include '_includes/nav' %}
<div>
{% block content %}
{% endblock %}
</div>
<footer class="main-footer">
<div class="footer-wrapper">
{{ exampleInformation.exampleDescription|markdown }}
<p>
© {{ now|date('Y') }}, Lorem Ipsum
</p>
</div>
</footer>
<script src="https://unpkg.com/leaflet#1.4.0/dist/leaflet.js" integrity="sha512QVftwZFqvtRNi0ZyCtsznlKSWOStnDORoefr1enyq5mVL4tmKB3S/EnC3rRJcxCPavG10IcrVGSmPh6Qw5lwrg==" crossorigin=""></script>
{% includeJsFile siteUrl ~ 'assets/js/scripts.js' %}
</body>
</html>
Through the control panel in craft CMS I created an entry that contains a table that has a series of coordinates (longitude and latitude values) and a lightswitch to toggle it on or off, along with some other general information for each entry that is created e.g. title, date, image, etc. on a separate tab.
The _entry.twig page extends _layout.twig -> _entry.twig:
{% extends '_layout' %}
{% set featureImage = {
mode: 'crop',
width: 600,
height: 600,
quality: 90
} %}
{% block content %}
<div class="entry__container">
<div class="entry__wrapper">
<div class="entry__title">
<h1>
{{ entry.title }}
</h1>
</div>
<div class="entry__image">
{% if entry.featureImage|length %}
{% for image in entry.featureImage.all() %}
<img src="{{ image.getUrl(featureImage) }}"
alt="{{ image.title }}" />
{% endfor %}
{% endif %}
</div>
<div>
{% for block in entry.exampleContent.all() %}
<div class="entry__description">
{% if block.type == 'text' %}
{{ block.text }}
{% elseif block.type == 'image' %}
{% for image in block.image.all() %}
<img src="{{ image.url }}" alt="{{ image.title }}" />
{% endfor %}
{% endif %}
</div>
{% endfor %}
</div>
{# display post categories #}
{% if entry.exampleCategories|length %}
<div class="entry__category">
<p>
Categories
</p>
{% for category in entry.exampleCategories.all() %}
{{- category.title -}}
{% endfor %}
</div>
{% endif %}
{# display table info #}
{% if entry.lotInfo|length %}
<div class="entry__coordinate">
<ul>
{% for row in entry.lotInfo %}
{% if row.createNewEntry == '1' %}
<li>
<div data-latCoordinate="{{ row.latitude }}"
id="latCoordinate">
{{ row.latitude }}
</div>,<div data-lngCoordinate="{{ row.longitude }}"
id="lngCoordinate">
{{ row.longitude }}
</div>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
{# end table info #}
</div>
</div>
{% endblock %}
I was able to put together this small section in _entry.twig which looks at the table and if the lightswitch is set to 1, it outputs the corresponding latitude and longitude values in the row:
{# display table info #}
{% if entry.lotInfo|length %}
<div class="entry__coordinate">
<ul>
{% for row in entry.lotInfo %}
{% if row.createNewEntry == '1' %}
<li>
<div data-latCoordinate="{{ row.latitude }}"
id="latCoordinate">
{{ row.latitude }}
</div>,<div data-lngCoordinate="{{ row.longitude }}"
id="lngCoordinate">
{{ row.longitude }}
</div>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
{# end table info #}
These values are currently displaying on the front end entry page which will show the coordinates depending on which lightswitch toggle is active- which has allowed me to ensure that it is pulling the correct coordinates corresponding to the correct information.
Now, I have an external js file linked which lives in local/craft/assets/js/*.js and contains this script -> scripts.js:
//Set initial map view
var map = L.map('map', { scrollWheelZoom: false }).setView(
[50.4205, -104.52],
15,
)
//Initialize the tilemap
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
minZoom: 14.5,
attribution:
'© OpenStreetMap contributors',
}).addTo(map)
// Set location array
var locations = [{ lat: 'SOME LATITUDE COORDINATE', lng: 'SOME LONGITUDE COORDINATE' }]
function addToMap(locationArray) {
//Iterate through array object
;[].forEach.call(locationArray, function (location) {
var marker = L.marker([location.lat, location.lng]).addTo(map)
})
}
//Show markers
addToMap(locations)
Currently, this script will create a leaflet/osm map and then based on:
// Set location array
var locations = [{ lat: '(SOME LATITUDE VALUE)', lng: '-(SOME LONGITUDE VALUE') }];
will output a marker to the map (currently only works if I manually insert lat and long coordinates) which lives in my index.twig template file -> index.twig:
{% extends '_layout' %}
{% set posts = craft.entries.section('example').all() %}
{% block content %}
<div class="background-test">
<div id="map"></div>
</div>
<div class="main-container">
<div class="main-wrapper">
<h1 class="example-title">
Some Title
</h1>
<div class="category-list">
{% set entries = craft.entries.limit(null) %}
{% for category in craft.categories.relatedTo(entries).order(
'title asc'
) %}
{% set entryCount = entries.relatedTo(category).total() %}
<a href="{{ category.url }}">
{{- category.title -}}<span class="count-number">({{ entryCount }})</span>
</a>
{% endfor %}
</div>
{% include '_includes/listing' with {
posts: posts
} only %}
</div>
</div>
{% endblock %}
What would be the best way to somehow use the
{{ row.latitude }}, {{ row.longitude }}
variables from my _entry.twig file in my existing scripts.js file to place a marker(s) on the map which lives on the index.twig page? I am still new to Craft and more so with Twig so I am still in the process of learning these things.
My folder structure is:
/assets
/css
/js
scripts.js
/templates
/includes
/blog
_category.twig
_entry.twig
index.twig
_layout.twig
index.html
Any help would be greatly appreciated!
Thank you
Ok some pointers first, you can add multiple data-* attributes to one element, also id's need to be unique. So I'd suggest you'd swap up the twig template to the following:
{% if entry.lotInfo|length %}
<div class="entry__coordinate">
<ul>
{% for row in entry.lotInfo %}
{% if row.createNewEntry == '1' %}
<li data-latCoordinate="{{ row.latitude }}" data-lngCoordinate="{{ row.longitude }}">
{{ row.latitude }}, {{ row.longitude }}
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
Then we need to adjust your javascript to be able to add multiple dots on the map.
First remove the following line
// Set location array
var locations = [{ lat: 'SOME LATITUDE COORDINATE', lng: 'SOME LONGITUDE COORDINATE' }]
As you've defined the pointers in html we just need a selector that selects all the elements where the data-* attributes are defined in
<script>
document.querySelectorAll(".entry__coordinate ul li").forEach(row => {
addToMap({ 'lat': row.dataset.latcoordinate, 'lng' : row.dataset.lngcoordinate, });
});
</script>

running javascript in a nested component-like django partial template

I would like to make a "component" (several actually, but let us start with one). That is I would like to have a template file, which itself may include javascript. Then I would like to be able to include that "component" in whatever (other) Django template file.
Importantly: in the base.html I include utility javascript (like jquery or bootstrap or whatever) and I want those things to be in scope in the component's javascript.
Are there other achitectural ways of achieving this?
Here is a visual of one Django template:
and when an item is clicked, it will update the other part of the page, allowing that template and its included JS to run with access to the rest of the page's javascript.
Here is some code to go along with it (I mistyped base to baste, so I went with the theme):
models.py
class CommonTask(models.Model): ## nothing special
name = models.CharField(max_length=30, default='yummy')
urls.py
app_name = 'food'
urlpatterns = [
## the list view on the left
url(r'^edible/?', views.EdibleList.as_view(), name='edible'),
## the partial on the right
url(r'^billable/edit/(?P<jid>[a-zA-Z0-9-\w:]+)/?', views.EdibleEdit.as_view(), name='edibleEdit'),
views.py
class EdibleList(ListView):
template_name = 'food/edible-list.html'
def get_queryset(self):
return Dish.objects.filter('edible'=True)
class EdibleEdit(UpdateView):
form_class = EdibleForm
template_name = 'food/edible-edit.html'
def get_initial(self):
… # for the form/formset info
def get_object(self):
return get_object_or_404(Dish, pk=self.kwargs['pk'])
baste.html
<!DOCTYPE html>
{% load static %}
<html lang="en" xml:lang="en" dir="ltr" xmlns= "http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" href="{% static "css/main.css" %}">
{% block meta_tags %}{% endblock meta_tags %}
{% block scripts-head %}{% endblock scripts-head %}
<title>Edibles - {% block title%}{% endblock title%}</title>
{% block extra_head %} {% endblock extra_head %}
</head>
<body>
{% block content %} {% endblock content %}
{% block scripts %} {% endblock scripts %}
<script>
{% block script-inline %} {% endblock script-inline %}
</script>
<footer> FOOTER </footer>
</body>
</html>
list.html
{% extends "baste.html" %}
{% load static %}
{% block title%}Edible List - {{dish.name}} {% endblock title%}
{% block extra_head %}
<link rel="stylesheet" href="{% static "food/food.css" %}">
{% endblock extra_head %}
{% block script-inline %}
console.log('This works, as it is in the views original compiled template');
console.log('This does not work, as it relies on the partial to be loaded, but
the partial isn't loaded yet, and this wont update after the partial is loaded);
var interpuncts = document.getElementsByClassName("interpuncts");
for (let i=0; i < interpuncts.length; i++){
interpuncts[i].onclick=function(event){
console.log('gh1');
};
};
// showing the right partial when clicked
pane = document.getElementById("edible-single");
showPane = function(link) {
fetch(link).then(function(response) {
return response.text();
}).then(function(body) {
pane.innerHTML = body;
});
};
let edibleLinks = document.querySelectorAll("a.edible-link");
edibleLinks.forEach(function(edibleLink) {
edibleLink.addEventListener('click', function(e){
e.preventDefault();
showPane(edibleLink.getAttribute('href'));
});
});
{% endblock script-inline %}
{% block scripts %} {% endblock scripts %}
{% block content %}
{% include "food/nav.html" with currentView="edibleList" %}
<div class="container-fluid">
<h1 class="text-center">Edible Dishes</h1>
<hr>
<div class="row scrollable-row">
<div class="col-3 scrollable-col">
<table class="table">
<tbody>
{% for dish in dish-list %}
<tr class="d-flex">
<td class="col">
<a class="edible-link"
href="{% url 'food:ediblebleDetail'
pk=dish.pk %}"
data-pk="{{dish.pk}}">{{dish.name}}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="col-9 scrollable-col" id="edible-single"></div>
</div>
</div>
{% endblock content %}
editPartial.html
{% load static %}
{# no blocks work in this, because it can't extend the list.html, because the view #}
{# can't render to endpoint(sibling) templates at the same time #}
{# {% block * %} #}
<script>
console.log('This won\'t run, as it is loaded separately as just an inclusion
text from the list AJAX call.')
$(document).ready(function() {
console.log('This can\'t work because it relies on $ to be accessible, which it is not')
var interpuncts=document.getElementsByClassName("interpuncts");
for (let i=0; i < interpuncts.length; i++){
interpuncts[i].onclick=function(event){
console.log('I can not get a handle on the partial template items after
they are loaded as a partial');
};
};
});
</script>
<div class="container">
Dish - {{dish.name}}
<hr>
<form action="{% form url %}" method="post">
{% csrf_token %}
{{ form }}
{# Some element(s) that needs some javascript to act on it, more comprehensive than #}
{# bootstrap show/hide can do #}
<div class="interpuncts">
··· Do something with this element
</div>
<input class="btn btn-primary pull-right" type="submit" value="Save">
</form>
</div>
NOTES:
This is similar to Rails' partials if I remember, and definitely doable in 'Angular/Ember'. I think this boils down to some architecture, or package I am unaware of and can't find documentation. I am unsure of it is doable with inclusion tags.

Categories