Reusable HTML component libraries in Django - html

I have a Django website with many pages all with their own unique content and markup (as opposed to a single template that is dynamically filled with data). These pages' templates are derived from a common "tool" template, which in turn derives from a "base" template. Each page also has a JS file which does the scripting for that page. Each pages data is spread around 3 or 4 locations:
the template
the page script (and maybe other page-specific statics)
a central "tool registry" which contains thing like the tool name, description and categories
for some tools, a set of template tags to do some HTML construction specific to that page (e.g. data to present in a table).
However, although each page has a unique layout and content, I still want to be able to share commonly used parametrised HTML "snippets" between pages. For example, one page has an input with a dropdown button that, using Bootstrap, looks like this:
<div class="input-group">
<span class="input-group-btn">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">
Common baudrates
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-right" id="common-baudrate-list">
<li><a href='#'>9600</a></li>
<li><a href='#'>115200</a></li>
</ul>
</span>
<input class="form-control" type="text" id="baudrate-input" val="115200">
</div>
I'd like to make a re-uasable parametrised function/template/tag which allows me to put in a structure like this given something like "group ID", "list of entries", "left/right button position", "default value".
The ways I see to do it so far are:
As a template, which has a fairly limited syntax, and make it hard to do something like a list of entries like this: ['Entry 1', 'Entry 2']
As a template, passing data into the context via the view (doesn't really appeal because I'd be spreading the pages contents around even more).
As a template tag, building the HTML as a big nasty string
As a template tag using an XML library like lxml, which is a pretty flexible method, but suffers from verbose syntax, over-complexity and I still have get data into the tag from the template!
None of these seem like a neat, re-usable and loosely coupled way to deal with this (for example, if I change up to Bootstrap 4 in future, I may need to re-write these components, and I'd rather have to do it just once). Having a library of components like this will also make future pages easier to construct. Is there a canonical "right" way to do this, or even a commonly used idiom for it?
Edit to show solution implementation
Using inclusion tags as answered below by Wolph, I avoided using a clunky construction like this
{% with [1,2,3] as items %}
{% my_tag items.split %}
{% endwith %}
as follows:
#register.inclusion_tag('components/this_component.html')
def input_with_dropdown(dropdown_title, dropdown_items, default_value, group_id, button_left, *args, **kwargs):
split_char = kwargs['split_char'] if 'split_char' in kwargs else ' '
return {
'dropdown_items': dropdown_items.split(split_char),
'dropdown_title': dropdown_title,
'group_id': group_id,
'default_value' : default_value,
'button_left': button_left
}
And then passing in the variables like this:
{% input_with_dropdown 'Common baudrates' '9600 48000 115200' 115200 'baudrate-input' 0 %}

Have you taken a look at inclusion tags? https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#inclusion-tags
I believe that these would do exactly what you want, you can still specify parameters but you can just pass along a template to have it render really easily.
Example:
from django import template
register = template.Library()
#register.inclusion_tag('list_results.html')
def list_results(results):
return {
'results': results,
'count': len(results),
}
list_results.html:
Found {{ count }} results.<br>
<ul>
{% for result in results %}
<li>{{ result }}</li>
{% endfor %}
</ul>

Use templatetags and html template... Then you can reuse that HTML template...

Related

Can a liquid for loop contain a page variable in Jekyll?

Let's say I have a bunch of _data files that I use to create a list for specific pages. All of the pages that have these lists have a custom front matter variable of pageName. The value of pageName just so happens to match the _data file with the list.
Rather than pasting the html blocks of code into each page, I'd like to use an include to pull in a generic block of html. To do that, the include has to be dynamic or contain liquid markup to become dynamic.
That might be more context than you need, but my question boils down to this, is something like this possible:
{% for item in site.data.{{page.pageName}} %}
{{ item.label }}
{% endfor %}
Unless I'm missing something, this doesn't work so I'm wanting to know if there's a way of achieving similar functionality.
I found this similar question, but it doesn't seem to answer whether it can be used to access a page variable to build a file name.
You can use the bracket notation like this :
{% for item in site.data[page.pageName] %}

How do I inspect Pelican variables

I'm modifying a Pelican template and I have the code below which adds url every time a page is found. I can see that the p object has the attributes url and title.
However I only knew this because I copied the code from another template shown below. Is there any way to inspect objects in jinja2 or Pelican to understand what information is contained within them?
{% for p in pages %}
<h1 class = "sidebar-title">
<a href="{{ SITEURL }}/{{ p.url }}">
{{ p.title }}
</a>
</h1>
https://github.com/getpelican/pelican-themes/blob/master/backdrop/templates/base.html
<li{% if p == page %} class="active"{% endif %}>{{ p.title }}</li>
I am not aware of an official resource explaining all variables, objects, attributes and properties in detail.
But for a start, I think the following start points suffice:
Common variables available for the standard templates
pelican.contents.py:
This is the module which contains (most of) the data structures pelican uses and which are made available in the templates. Have a look for properties (#property, these are functions which act like they are attributes) and attributes. At lines 367ff there are some very simple subclass definitions which could be of use.
pelican.writers.py: This module brings together the templating engine jinja2, the templates and the data to be inserted in the templates. Of special interest for you could be lines 138ff, as this seems like a good point to simply insert some small debug prints to see the real data which is present in the data structures.

Twig escaping HTML and rendering as raw

I hope someone can assist me on this issue.
I am pulling details from a database to display on a twig template (using Symfony2) but the way in which it is saved in the db makes it difficult to interpret the HTML.
Basically, the HTML tags are already translated as entities in the table, e.g.:
<p>Bach Flower Crab Apple Remedy:&nbsp;the "cleansing" Remedy can be used both internally and externally&nbsp;</p><p><strong>
And so on. I have researched the rendering options in twig and tried the following (based on me rendering a loop of product descriptions):
{% set strategy = 'html' %}
{% autoescape 'html' %}
{{ product.description|escape('html')|raw }}
{% endautoescape %}
and also just:
{{ product.description|raw }}
The first method just echoes the existing content (as entities) and the second method just renders the HTML tags to the page as follows:
<p>Bach Flower Crab Apple Remedy: the "cleansing" Remedy can be used both internally and externally.</p><p><strong>...
So, as you can see, I cannot find a way to actually interpret the HTML tags in order to display the description as it should be.
Is there a way to do this? I can't do it in the PHP as all it's doing is sending an object to the template which is looped through:
public function showAction(Request $request, $store_id=0)
{
$max = 1000;
$repository = $this->getDoctrine()->getRepository('AppBundle:Product');
$products = $repository->getProductsByStoreId($store_id,$max);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$products,
$request->query->get('page', 1),
20
);
$return['products'] = $pagination;
$return['categories'] = $this->getCategories();
return $this->render('AppBundle:tables:productstable.html.twig', $return);
}
Your core issue is that you do not have HTML in your database to begin with. At best Twig could be outputting some HTML entities, which will render visibly as "<p>...", and at "worst" Twig will escape the text to render it accurately as it actually is, which is "<p>...". Expecting Twig to output actual HTML which will render a paragraph is unrealistic, since that's not what your original data contains at all.
You'll have to HTML-decode that text in PHP first, and then output it in Twig with ..|raw. raw means that Twig will output it as is without further escaping it. Since it's nonsense to get the data from the database to then html_entity_decode it, you need to fix your data input here! Don't HTML encode data which is going into the database, it serves no purpose.
I think you have to write custom escaper plugin to decode html entities and use it like this:
{{ product.description|myawesomehtmlentitiesdecoder|raw }}
http://twig.sensiolabs.org/doc/filters/escape.html#custom-escapers for reference.
But generally, it's better to store HTML in database and then apply needed security filters on output.

Is there a way to evaluate string with liquid tags

I need to provide page content reference list (it should contain references on sections on page).
The only way which I can see is to use page.content and parse it, but I stumbled on problem with data evaluation. For example I can pull out this string from page.content: {{site.data.sdk.language}} SDK but there is no way to make jekyll process it, it outputs as is.
Also I want to make it possible to create cross-pages links (on specific section on page, but that link generated by another inclusion and doesn't persist in page.content in HTML form).
Is there any way to make it evaluate values from page.content?
P.S. I'm including piece of code which should build page content and return HTML with list (so there is no recursions).
P.P.S. I can't use submodules, because I need to run this pages on github pages.
Thanks.
Shouldn't {{ site.data.sdk.language | strip_html }} do it? I don't know, most probably I didn't understand the problem. Can you elaborate more? Maybe provide a link to a post you're referring to?
Thinking about the similar
{% assign title = site.data.sdk.language %}
which is a stock Liquid tag and does the job well, so instead of
{% section title={{site.data.sdk.language}} %}
write your code as
{% section title = site.data.sdk.language %}
The key here is that once you enter {%, you're in Liquid. Don't expect Liquid to go "Inception" all over itself. {{ is just a shorthand for "print this to output", but an parameter passing is not output, it's just reading a variable.
You should be able to also go wild and do:
{% section title = site.data.sdk.language | capitalize %}
For more re-read the docs: https://github.com/Shopify/liquid/wiki/Liquid-for-Designers

Octopress HTML Includes with Arguments

I would like to include HTML snippets within blog posts and have the code in a single place. After perusing the framework I've been able to get this to work:
source
_includes
custom
foo.html
{% include custom/foo.html %}
My question: Is there a way that I can pass arguments to foo.html? I simply want to pass some strings in and output them in a formatted way and cannot figure out what I'm missing.
Thanks for your time.
You can't. But you can use some other way likes what theme does to do this.
First, set formatted style you want in custom/foo.html, for example, I add a customfooter in source/_includes/custom/footer.html:
<p>
Copyright © {{ site.time | date: "%Y" }} - {{ site.author }} -
<span class="credit">Powered by Octopress</span>
{{ site:customfooter }}
</p>
Then, set the string you want to set in _config.yml:
url: http://fann.im
customfooter: My custom footer, bala bala.
Hope this will help you.
There's no way to do this because Jekyll is static. By definition anything you could pass from one file to another file is a known value, because it must be defined at the time of site generation. Since you can't pass dynamic values, this sort of indirection doesn't make sense because you can just put the static value where it is going to be anyway.
If you have the use case of e.g. generating 10 chunks of output that are mostly similar but differ slightly, then use a plugin to accomplish the task of isolating those changes using variables. This gives you some flexibility while still putting the value into the template where it will be used.
Here's an example of a liquid tag that abstracts generating twitter bootstrap nav tabs with specific hrefs assigned.
module Jekyll
class XmlJsonTabsTag < Liquid::Tag
def initialize(tag_name, markup, tokens)
#methodname = markup.strip
super
end
def render(context)
#wondering what this syntax is? google "here document"
<<-HTML
<ul class="nav nav-tabs">
<li class="active">
JSON
</li>
<li>
XML
</li>
</ul>
HTML
end
end
end
Liquid::Template.register_tag('xmljsontabs', Jekyll::XmlJsonTabsTag)
Pass the value to use for #methodname in the liquid tag in the template:
{% xmljsontabs foo %}
Which outputs something like
<ul class="nav nav-tabs">
<li class="active">
JSON
</li>
<li>
XML
</li>
</ul>