Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow block to be used multiple times #265

Open
faulesocke opened this issue Mar 1, 2018 · 23 comments
Open

Allow block to be used multiple times #265

faulesocke opened this issue Mar 1, 2018 · 23 comments

Comments

@faulesocke
Copy link

It should be possible to use a block multiple times in the template. This is possible in jinja2, because a block can be accessed through the special self variable as described for example here:

https://stackoverflow.com/questions/20929241/how-to-repeat-a-block-in-a-jinja2-template

@Keats
Copy link
Owner

Keats commented Mar 3, 2018

I have no strong feeling about that. I don't really see the usecase though so I'd like to see more people wanting that

@faulesocke
Copy link
Author

Probably the most common use case is for the page title as seen in the stackoverflow thread. Of course one can easily work around this, but I thought it would be nice, if I (and others) don't have to.

@paulcmal
Copy link
Contributor

paulcmal commented Aug 6, 2018

Probably the most common use case is for the page title as seen in the stackoverflow thread.

Yes, that's a very common use-case. I also had to scratch my head around it when making a Hugo theme. I just don't understand the logic of not being able to reuse a block elsewhere in the template ^^" (is there an actual reason?)

@Keats
Copy link
Owner

Keats commented Aug 6, 2018

I just don't understand the logic of not being able to reuse a block elsewhere in the template ^^" (is there an actual reason?)

In the first template of the stackoverflow, thetitle block is defined twice. When using the title block in a child template, which one should the template engine inherits from? The template engine has no way of knowing which one so that's the reason you go through self in Jinja2

@paulcmal
Copy link
Contributor

paulcmal commented Aug 6, 2018

that's the reason you go through self in Jinja2

Thanks for the explanation! Am i wrong to think this could also be addressed differently by using a separate function for overriding the block? (such as define in Hugo)

@Keats
Copy link
Owner

Keats commented Aug 6, 2018 via email

@Keats Keats mentioned this issue Sep 6, 2018
16 tasks
@paulcmal
Copy link
Contributor

paulcmal commented Sep 6, 2018

What you need is a way to render the content of a block, not redefine it.

Yes, I meant that we need different keywords for defining and using a block. Sorry if i wasn't clear. I understand that it would make sense in order to avoid a breaking change to have define a {% useblock BLOCK %} feature. Would that be hard to implement?

I think the title example from stackoverflow is a clear example of why people would want this. Are variables the appropriate way to deal with such issues? (I'm not sure about the flow of data of a template to/from other templates it extends)

@faulesocke
Copy link
Author

I would definitely propose the useblock syntax since this ambiguity is one of the most disgusting things in jinja2.

@Keats
Copy link
Owner

Keats commented Nov 29, 2018

Woops didn't see @paulcmal message

I think useblock makes sense and shouldn't be too hard to implement

@BertalanD
Copy link

What's wrong with the {{ self.block_name() }} syntax?

@rpearce
Copy link

rpearce commented Mar 5, 2021

Here's an example where title, description, site and url are used >1 time on a regular page that doesn't want to look like garbage on social media:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>{% block title %}{% endblock title %}</title>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="description" content="{% block description %}{% endblock description %}" />
  <meta name="author" content="{% block author %}{% endblock author %}" />
  <meta name="keywords" content="{% block keywords %}{% endblock keywords %}" />
  <meta name="apple-mobile-web-app-capable" content="yes" />

  <meta property="og:site_name" content="{% block site %}{% endblock site %}" />
  <meta property="og:title" content="{% block title %}{% endblock title %}" />
  <meta property="og:url" content="{% block url %}{% endblock url %}" />
  <meta property="og:description" content="{% block description %}{% endblock description %}" />

  <meta property="og:image" content="{% block og_image_src %}{% endblock og_image_src %}" />
  <meta property="og:image:alt" content="{% block og_image_alt %}{% endblock og_image_alt %}" />
  <meta property="og:type" content="{% block type %}{% endblock type %}" />

  <meta property="twitter:card" content="summary_large_image" />
  <meta property="twitter:site" content="{% block site %}{% endblock site %}" />
  <meta property="twitter:title" content="{% block title %}{% endblock title %}" />
  <meta property="twitter:description" content="{% block description %}{% endblock description %}" />
  <meta property="twitter:image" content="{% block twitter_image_src %}{% endblock twitter_image_src %}" />
  <meta property="twitter:creator" content="{% block twitter_author %}{% endblock twitter_author %}" />

  <link rel="canonical" href="{% block url %}{% endblock url %}" />

  {% block extra_head %}
  {% endblock extra_head %}
</head>
<body>
  {% block content %}{% endblock content %}
</body>
</html>

Naturally, this breaks on the "can't have multiple whatevers".

Do y'all have a suggested workaround for this?

@Keats
Copy link
Owner

Keats commented Mar 5, 2021

You can set the variables once depending on the variables present and render them.
Eg

{% if page %}
{% set title = page.title %}
{% else %}
...
{% endif %}
...
<meta property="og:title" content="{{title}}" />

@rpearce
Copy link

rpearce commented Mar 5, 2021

Thanks for the quick reply, @Keats! I'll give this a roll later today

@rpearce
Copy link

rpearce commented Mar 10, 2021

@Keats Is what you suggested possible in a template that is being extended from another template? For example, what is above is my base.html, and here is post.html:

{% extends "base.html" %}
{% block author %}{{author}}{% endblock author %}
{% block description %}{{description}}{% endblock description %}
{% block keywords %}{{keywords}}{% endblock description %}
{% block site %}{{site}}{% endblock site %}
{% block title %}{{title}}{% endblock title %}
{% block twitter_author %}{{twitter_author}}{% endblock description %}
{% block type %}article{% endblock type %}
{% block url %}{{url}}{% endblock url %}
{% block content %}
<!-- some stuff here -->
{{content_html | safe}}
{% endblock content %}

and I'm trying to get base.html to use these values of title, description and url in a few places in base.html

@Keats
Copy link
Owner

Keats commented Mar 10, 2021

Yep, it should work

@rpearce
Copy link

rpearce commented Mar 11, 2021

I can't seem to get that concept to work when I'm rendering post.html with data, providing it to a block, and trying to use it a few times in base.html.

I'm not rendering base.html, but I am extending it and trying to send it variables.

I do think it is this same issue of "Allow block to be used multiple times". How else can you render template2, which extends template1, and have template1 use an inserted variable from template2 more than 1 time?

Thank you for your time, and I can stop bothering you if this is annoying!

@Keats
Copy link
Owner

Keats commented Mar 11, 2021

The way the template engine works, it will start rendering the base template (base.html) in all cases going through blocks. So if you define anything in the base.html, whether you're rendering template2 or template1 doesn't matter since the context will be what you care about.
If you have an example repo I can have a look

@LunNova
Copy link

LunNova commented Oct 27, 2021

It looks like rpearce was trying to set something in post.html which extends from base.html which would then need to be used in base.html.

If you're using Zola you run into this situation.

If I do something like this:

index.html:
<title>{% block title %}{{ config.title }}{% endblock title %}</title>

page.html (which extends from index.html):
{% block title %} {{ page.title }} | {{ config.title }} {% endblock title %}

It works for setting the title block contents from page.html.

If instead I use a variable:

index.html:

{% set title_example = config.title %}
<title>{{ title_example }}</title>

page.html:
{% set title_example = page.title ~ ' | ' ~ config.title %}

There are no errors, but the value of title_example used in page.html is not used.

In this example page.title is not available in index.html.

An alternative approach is:

page.html:

        {% set title_example = config.title %}
        {% if page and page.title %}
            {% set title_example = page.title ~ ' | ' ~ config.title %}
        {% endif %}

        <title>{{ title_example }}</title>
        <!-- other stuff here -->
        <meta name=”twitter:title” content=”{{ title_example }}” />

This approach requires hardcoding specific cases where the title is used into index.html instead of having them be the responsibility of the child templates so it isn't great either.

Blog repo, Theme and link to page.html

@Keats
Copy link
Owner

Keats commented Oct 28, 2021

What's wrong with the {{ self.block_name() }} syntax?

I find it hard to understand what self is there. Can I also render imported macros that way? Or render variable set in that template.

@theowenyoung
Copy link

@LunNova did you figure out this?

@LunNova
Copy link

LunNova commented Oct 20, 2022

Nothing better than the example at the bottom there :/

@theowenyoung
Copy link

Thanks @LunNova , I finally handle all these cases on base.html

 {%- block social_meta -%} 
  {%- if page.title -%}
    {%- set_global title = page.title -%}
  {%- elif term.name -%}
    {%- set titled_type_name = taxonomy.name | title -%}
    {%- set_global title = titled_type_name ~ " | " ~ term.name  -%}
  {%- elif taxonomy.name  -%}
    {%- set_global title = taxonomy.name | title -%}
  {%- elif section.title -%}
    {%- set_global title = section.title -%}
  {%- endif -%}
  {%- if title -%}
    {%- set_global full_title = title ~ " - " ~ config.title -%}
  {%- elif is_index == true -%}
    {%- set_global title = config.title -%}
    {%- set_global full_title = config.title -%}
  {%- endif -%}
  {%- if page.description -%}
    {%- set_global description =  page.description -%}
  {%- elif page.summary -%}
    {%- set_global description =  page.summary | spaceless | striptags -%}
  {%- elif page.content -%}
    {%- set_global description = page.content | spaceless | striptags | truncate(length=200) -%}
  {%- elif is_index -%}
    {%- set_global description = config.description -%}
  {%- elif section.content -%}
    {%- set_global description = section.content | spaceless | striptags | truncate(length=200) -%}
  {%- else -%}
    {%- set_global description = config.description -%}
  {%- endif -%}
  {%- if page.extra.image -%}
    {%- set_global image = page.extra.image -%}
  {%- elif is_index and config.extra.image -%}
    {%- set_global image = config.extra.image -%}
  {%- elif page.assets -%}
    {%- for asset in page.assets | sort -%}
      {%- if asset is matching("[.](jpg|png|jpeg)$") -%}
          {%- set_global image=get_url(path=asset) -%} 
          {%- break -%}
      {%- endif -%}
    {%- endfor -%}
  {%- endif -%}
<title>{{full_title}}</title>
  <meta property="og:title" content="{{title}}"/>
  <meta itemprop="headline" content="{{title}}"/>
  <meta property="og:description" content="{{description}}"/>
  <meta name="description" content="{{description}}"/>
<meta property="og:url" content="{{current_url | safe}}"/>
  {%- if image -%}
  <meta name="og:type" content="summary_large_image"/>
  <meta property="twitter:card" content="summary_large_image"/>
  <meta property="og:image" content="{{ image }}"/>
  <meta property="og:image:alt" content="{{ title }}"/>
  {%- else -%}
  <meta name="og:type" content="summary"/>
  {%- endif -%}
{%- endblock social_meta -%}

@Keavon
Copy link

Keavon commented May 10, 2024

A Working Solution for Reusing Values

Instead of trying to do this, which isn't allowed:

<!-- base.html -->

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- We can't use the same `title` block twice -->
  <title>{% block title %}{% endblock title %}</title>
  <meta property="og:title" content="{% block title %}{% endblock title %}" />
</head>
<body>
  {% block content %}{% endblock content %}
</body>
</html>
<!-- page.html -->

{% extends "base.html" %}

{% block title %}{{ page.title }}{% endblock title %}

{% block content %}{{ content_html | safe }}{% endblock content %}

We can instead do this:

<!-- base.html -->

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- This imports the variables that are set in the block in page.html
       and they're accessible anywhere below this block/endblock line -->
  {% block head_variables %}{% endblock head_variables %}

  <title>{{ title }}</title>
  <meta property="og:title" content="{{ title }}" />
</head>
<body>
  {% block content %}{% endblock content %}
</body>
</html>
<!-- page.html -->

{% extends "base.html" %}

{% block head_variables %}
{% set title = page.title %}
<!-- You can set more variables here as you wish -->
{% endblock head_variables %}

{% block content %}{{ content_html | safe }}{% endblock content %}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants