I'm trying to understand the language of Nunjucks templates.
To generate HTML I use Gulp + plugin gulp-nunjucks-render.
Faced a problem: I can not understand how to implement the generation of meta tags title and description.
Project file structure:
project
|
| -> dist > index.html (compiled)
| page1.html (compiled)
| page2.html (compiled)
|
|
| -> src
|-> pages -> index.njk (home page)
| page1.njk (page 1)
| page2.njk (page 2)
|
|-> templates
| |-> layout.njk
| |
| |-> parts -> header.njk
| footer.njk
|
|-> styles -> style.css
| style.min.css
|
|-> data.json
layout.njk
<!-- layout.njk -->
<html lang="en">
<head>
<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>
Which I connect to each page as follows:
{% extends "layout.njk" %}
index.njk
{% 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
{% extends "layout.njk" %}
{% block header %} {% include "parts/header.njk" %} {% endblock %}
{% block main %}
<main class="main">
<!-- content page1 -->
</main>
{% endblock %}
{% block footer %} {% include "parts/footer.njk" %} {% endblock %}
page2.njk
{% extends "layout.njk" %}
{% block header %} {% include "parts/header.njk" %} {% endblock %}
{% block main %}
<main class="main">
<!-- content page1 -->
</main>
{% endblock %}
{% block footer %} {% include "parts/footer.njk" %} {% endblock %}
I couldn't find any examples of title output in the documentation, so the most an interesting way to I saw here.
For example, get the title and description from a file .JSON
data.json
"pages": [
{
title: "Hompage"
description: "This is the home page"
},
{
title: "Page1"
description: "This is page 1"
}
{
title: "Page2"
description: "This is page 2"
}
]
gulpfile.js
const gulp = require('gulp');
const nunjucksRender = require('gulp-nunjucks-render');
const data = require('gulp-data');
gulp.task('nunjucks', function() {
return gulp.src('src/pages/**/*.njk')
.pipe(data(function() {
return require('./src/data.json')
}))
.pipe(nunjucksRender({
path: ['src/templates']
}))
.pipe(gulp.dest('docs'))
.pipe(browserSync.reload({stream: true}));
});
gulp.task('watch', function(cb) {
gulp.parallel(
'nunjucks',
)(cb);
gulp.watch('src/**/*.njk', gulp.series('nunjucks'));
});
gulp.task('default', gulp.series('watch'));
I don't know how to extract data from json. Please advise a solution.
If you are hoping to pass the data from data.json file
Step 1: you need to use someway to specify the page name in the data file itself.
Eg: data.json
{
"pages": {
"home": {
"title": "Hompage",
"description": "This is the home page"
},
"page1": {
"title": "Page1",
"description": "This is page 1"
},
"page2": {
"title": "Page2",
"description": "This is page 2"
}
}
}
Step 2: You have to set the page name as a variable in the nunjucks page.
Eg: index.njk
{% set pageName = 'home' %}
{% 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 %}
Step 3: Use the pageName variable in any nunjucks page, layout or partial to get the specific data for a specific page.
Eg: layout.njk
<html lang="en">
<head>
<title>{{ pages[pageName].title }}</title>
<link rel="stylesheet" href="styles/style.css">
</head>
<body class="page">
{% block header %}{% endblock %}
{% block main %}{% endblock %}
{% block footer %}{% endblock %}
</body>
</html>
I didn't compile and test this code. So make sure all variables and code syntax are correct if you are directly copying this code. The logic must work based on the documentation
Since you extends layout.njk in your pages, you could set the title and the description variables at the top of the document like so:
{% set title = myTitle %}
{% set description = myDescriptionHere %}
Then you do that for every pages (index.njk, page1.njk, page2.njk, etc.)
Alternatively, you could define your variables in nunjucksRender in the gulpfile.js as so:
.pipe(nunjucksRender({
path: ["yourPath"],
title: "yourTitle",
description: "yourDescriptionHere"
}))
// Rest of the code...
I didn't test it, I just deduce that from de documentation and great articles:
Killer features of Nunjucks
and
Building a static website with components using Nunjucks.
When you use extends, you should redefine block to override it
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
var html = `
{% extends "layout.njk" %}
{% block header %}
{{pages[1].title}}
{% endblock %}
{% block main %}
{{pages[1].description}}
{% endblock %}
`
var data = {
pages: [
{
title: "Hompage",
description: "This is the home page"
},
{
title: "Page1",
description: "This is page 1"
},
{
title: "Page2",
description: "This is page 2"
}]
}
res = nunjucks.renderString(html, data);
console.log(res);
Related
I am creating an eleventy scaffolding project to learn more about SSG, this is my first time using an SSG so I am new to the templating stuff with nunjucks, etc.
What I am trying to build:
I want to create a template for a landing page/showcase that can be re-used but display different data within a for loop.
What I have:
site.json
{
"showcase": [
{
"title": "explore docs",
"url": "#"
},
{
"title": "visit github repository",
"url": "https://github.com/ExampleUser/ExampleRepo"
}
],
"404showcase": [
{
"title": "return home",
"url": "/"
}
]
}
pageShowcase.njk
Currently, I am just using an if statement to control what data set to
use but if I ever extend this to other layouts and use a third
or fourth set of data this will become really repetitive adding in a
new elseif for every new set of data
{%- set pageTitle %}{% if title %} {{title}} {% else %} {{ site.title }} {% endif %}{% endset -%}
{%- set pageDescription %}{% if description %} {{ description }} {% else %} {{ site.description }} {% endif %}{% endset -%}
<!-- Showcase -->
<div class="container">
<div>
<div>
<h1>{{ pageTitle }}</h1>
<p>{{ pageDescription }}</p>
<div class="navbar" id="navmenu">
<ul class="navbar">
{% if defualt %}
{% for item in site.showcase %}
<li>
{{ item.title}}
</li>
{% endfor %}
{% else %}
{% for item in site.404showcase %}
<li>
{{ item.title}}
</li>
{% endfor %}
{% endif %}
</ul>
</div>
</div>
<!-- <img src="assets/img/#.svg" alt="temp"> -->
</div>
</div>
</section>
index.njk
---
layout: base
defualt: true
---
<!-- include pageShowcase -->
{% include "partials/pageShowcase.njk" %}
<!-- home page content -->
<section>
<div class="container">
<main>
{{content | safe}}
</main>
</div>
</section>
404.njk
---
title: "404: Page Not Found"
description: "It looks like the page doesn not exist"
defualt: false
---
<!DOCTYPE html>
<html lang="{{ lang }}">
<!-- include head -->
{% include "partials/head.njk" %}
<body>
<!-- use pageShowcase for 404 page -->
{% include "partials/pageShowcase.njk" %}
</body>
</html>
This is the best I could come up with on my own, but I feel like there has to be a better way without re-writing the same code over and over.
Appreciate any help I can get on this one.
I am a little confused about what {% extends base %} is extending at the start of index.html in Bokeh server application packages.
Examples of this can be seen in:
Bokeh Docs: Embedding in Templates
{% extends base %}
{% block contents %}
<div>
<p> Hello {{ user_id }}, AKA '{{ last_name }}, {{ first_name }}'! </p>
</div>
{% endblock %}
Bokeh Server Application Examples
Example code from the Gapminder package in templates/index.html
{% extends base %}
{% block title %}Bokeh Gapminder Example{% endblock %}
{% block postamble %}
<style>
{% include 'styles.css' %}
</style>
{% endblock %}
What is this "base" that is being extended?
I see that there is a "contents" block, "title" block, and "postamble" block from the above examples.
How do I know what other jinja blocks I can modify?
Thanks.
The Goal
I'm creating a Flask website to serve as the UI for a custom bioinformatics pipeline. The way I'm trying to design it is such that the user fills in the pipeline parameters (e.g. Project Name, Data Type, Number of Samples, etc.) in a multi-page form (kind of like the checkout process in online ordering) and then I would like these parameters to be saved to the user's account page as separate "posts".
The Issue
I've been following a Flask Tutorial online (https://www.youtube.com/watch?v=QnDWIZuWYW0&list=PL-osiE80TeTs4UjLw5MM6OjgkjFeUxCYH&index=2) about how to save the "posts" (pipeline parameters) to the user's dashboard by supplying fake or sample "posts" to the dashboard's html template, but nothing shows up at all. What am I doing wrong?
The Source Code
routes.py:
posts = [
{
'username': 'claudiadast',
'date_posted': 'September 22, 2018',
'stack_name': 'wgs-test-stack',
'start_point': 'fastq',
'input_uri': 's3://pipeline-validation/smallfq/',
'build': 'GRCh38',
'ome': 'wgs',
'project_id': 'summerwater598'
},
{
'username': 'claudiadast',
'date_posted': 'September 10, 2018',
'stack_name': 'clinical-exome',
'start_point': 'fastq',
'input_uri': 's3://pipeline-validation/Clinex/',
'build': 'GRCh38',
'ome': 'wes',
'project_id': 'summerwater598'
}
]
#app.route('/')
#app.route('/home')
def home():
return render_template('home.html', posts=posts)
home.html:
{% extends "layout.html" %}
{% block content %}
{% for post in posts %}
<h1>{{ post.stack_name }}</h1>
<p>{{ post.username }} on {{ post.date_posted }}</p>
<p>Pipeline Parameters:</p>
<p>{{ post.stack_name }}</p>
<p>{{ post.start_point }}</p>
<p>{{ post.input_uri }}</p>
<p>{{ post.build }}</p>
<p>{{ post.ome }}</p>
<p>{{ post.project_id }}</p>
{% endfor %}
{% endblock content %}
layout.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Psychcore Pipeline</title>
<link rel="stylesheet" href="static/site.css"
</head>
<body>
{% include 'includes/_navbar.html' %}
<div class="container">
{% block body %}{% endblock %}
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
<script src='https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js'></script>
</body>
</html>
In your layout.html you are defining a {% block body %}, however in your home.html you are using {% block content %}. Change one of these to match the other one and it should work.
To have jinja template read from a dict you need to change the syntax of all variables to the following in home.html:
{{ post['username'] }}
I am trying to use more different views in one template via different blocks or include method but I have got stuck. My aim is to implement my different views into one template. I tried to more solution but these haven't worked for me. I show these solution:
My views:
def dashboard_data(request):
# Here I collect data from my PostgreSQL database I push these into
#Objects and I send it to my test_a.html via render like below.
return render(request, 'my_project/test_a.html', send_data)
def chart_data(request):
#In these view my final goal is to collect data from database and create
#charts but for the simplicity I just use a list (a=[1,2,3,4]) and I
#push it to the test_b.html and I render it.
render(request, 'my_project/test_b.html', send_data))
My templates:
1, base.html
<!DOCTYPE html>
<html lang="en">
<head>
{% block title %}<title>My project</title>{% endblock %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Include CSS files -->
{% load static %}
<!-- Include CSS files -->
<link rel="stylesheet" href="{% static 'css/bootstrap.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
<link rel="stylesheet" href="{% static 'css/font-awesome.min.css' %}">
</head>
<body>
{% block sidebar %}
{% endblock %}
{% block content%}
{% endblock %}
</body>
</html>
1, test_a.html
{% extends "monitor/base.html" %}
{%block content%}
{%for i in table%}
<p> {{i.record}}</p>
{%endfor%}
{%endblock}
2, test b.html
{%for i in a%}
<p> {{i}}</p>
{%endfor}
So my goal would be that the test_b.html's result could appear in the base.html.
My solutions:
A: I tried to put into a block the test_b.html and integrate in the base.html
1, base.html (test1):
{% block conent%}
{% endblock %}
{% block chart%}
{% endblock %}
1, test_b.html (test1):
{% extends "monitor/base.html" %}
{%block chart%}
{%for i in a%}
<p> {{i}}</p>
{%endfor}
{%endblock%}
B: I tried to use {{ block.super }}
1, base.html (test1):
{% block conent%}
{% endblock %}
1, test_b.html (test1):
{% extends "monitor/base.html" %}
{%block content%}
{{ block.super }}
{%for i in a%}
<p> {{i}}</p>
{%endfor}
{%endblock%}
C: Finally I used include method too but I didn't work for me as well. Here I tried to use simple html tags like (<p>Hello world</p>) int the test_b.html and worked. So If I do not use variables it works but I have to use my variables from my views.
1, test_a.html
{% extends "monitor/base.html" %}
{%block chart%}
{%for i in a%}
<p> {{i}}</p>
{%endfor}
{%endblock%}
{% include "my_project/test_b.html" %}
2, test_b.html
{%for i in a%}
<p> {{i}}</p>
{% endfor %}
To include an HTML file from another file just use {% include 'htmlfilename.hml' %}
outside {% block your_block_name %}{% endblock %} if it's a base template.
You can learn more about it here.
<body>
{% include 'header.html' %}
{% block sidebar %}{% endblock %}
{% block content%}{% endblock %}
{% include 'footer.html' %}
</body>
If you want to use some functionality across several templates then use inclusion_tags.
As #alessioferri20 mentioned, you cannot render 2 views at the same time. Basically, one view corresponds to one HTTP request & response.
You can achieve what you want by using custom template tags:
https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/
but I have to use my variables from my views.
For example, chart_data could be an inclusion tag (https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/#inclusion-tags), here is some pseudo-code:
Let's say you want to show a chart for the age_stats from your view:
def your_view(request, ...):
age_stats = {...}
....
return render(request, "template...", {'age_stats': age_stats})
In your template:
{% chart age_stats %}
In your template tags:
#register.inclusion_tag('path-to/chart.html')
def chart(input_data):
# collect data from database & use the input_data coming from your view
data = ...
return {'data': data}
The {% include %} tag is how you insert other templates into blocks. These templates don't need to be registered in views.py or urls.py.
For example:
{% extends 'base.html' %} {% block home %} {% load static %}
<div>
<h1>Some Title</h1>
<p>Some text</p>
</div>
<div>
{% include 'some-other-template.html' %}
</div>
{% endblock %}
For more information: https://docs.djangoproject.com/en/4.1/ref/templates/builtins/
Ok super basic question but I must be missing something.
I render my page:
router.get('/', function(req, res) {
res.render('index.njk', {});
});
Create my base layout, main-layout.njk in layout folder:
<!DOCTYPE html>
<html>
<body>
{% block content %}
{% endblock %}
</body>
</html>
Extend my template, index.njk to add content to the layout:
{% extends 'layout/main-layout.njk' %}
{% block content %}
<?php include_once("analyticstracking.php") ?>
<main>
<p>sploooge</p>
{% block panel %}{% endblock %}
</main>
{% endblock %}
Now I would like index.njk to be extended by panel.njk (the file is in the same folder):
{% extends 'index.njk' %}
{% block panel %}
<p>second sploooge</p>
{% endblock %}
But I cannot get it to appear. The documentation doesn't mention anything but do I need to render it somehow like index.njk?
I believe this issue lies in where you have the 'middle' file.
I got this to work with the following configuration:
File Tree:
pages/
-index.njk
templates/
-base.njk
-layout.njk
base.njk:
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
{% block content %}{% endblock %}
</body>
</html>
layout.njk
{% extends "base.njk" %}
{% block content %}
Content
{% block more %}{% endblock %}
{% endblock %}
index.njk:
{% extends "layout.njk" %}
{% block more %}
More Content
{% endblock %}
Output:
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
Content
More Content
</body>
</html>
You can use a template that is anywhere, you just need to make sure you add that to the url in the extend. Example:
{% extends "templates/layouts/extras/template.njk" %}
This would get horribly confusing and increase the potential for error, but theoretically it would work.
Look into using includes in some areas as well, it may be beneficial in this case to not have an extra layer of templates.