Reputation: 11547
I was trying to make reusable interface components with django. I was able to pull something off with custom template tags:
{% panel %}
Some panel content
{% endpanel %}
It works fine but I wasn't able to pass the panel title as an argument. What I really wanted was something like:
{% panel 'Panel Title' %}
Some panel content
{% endpanel %}
I couldn't find an answer for this in the docs. Is there a way to achieve it? If not, is there another strategy that I could use?
edit: Keep in mind that the panel content is suposed to accept more markup inside, like other tags and blocks. That's why a simple template tag was not enough for me.
panel.html
<div class="panel panel-default">
<div class="panel-heading">{{ title|default:"Panel Title" }}</div>
<div class="panel-body">
{{ body }}
</div>
</div>
templatetags/theme.py
from django import template
register = template.Library()
@register.tag
def panel(parser, token):
nodelist = parser.parse(('endpanel',))
parser.delete_first_token()
return PanelNode(nodelist)
class PanelNode(template.Node):
def __init__(self, nodelist):
self.nodelist = nodelist
def render(self, context):
output = self.nodelist.render(context)
t = template.loader.get_template('theme/blocks/panel.html')
c = template.Context({'body': output})
return t.render(c)
Upvotes: 6
Views: 4212
Reputation: 9557
I've had the same problem, and I managed to do it after looking your question. Hopefully, this can help other people.
I'm using classy-tags on my project and I highly recommend it. The code could be simple if you're planning to use it only once. Since my intention is to have different kinds of panels, I've made it somewhat generic.
# templatetags/modals.py
from classytags.core import Tag, Options
from classytags.arguments import Argument, MultiKeywordArgument
from django import template
register = template.Library()
class BasePanel(Tag):
def render_tag(self, context, **kwargs):
nodelist = kwargs.pop('nodelist')
body = nodelist.render(context)
panel_context = self.get_panel_context(kwargs)
panel_context['body'] = body
t = template.loader.get_template(self.template)
c = template.Context(kwargs)
return t.render(c)
def get_panel_context(self, arguments):
"""
Override this method if you're using fancy arguments,
e.g: MultiKeywordArgument
"""
return arguments
class ExamplePanel(BasePanel):
name = 'panel'
template = 'main/modals/panel.html'
options = Options(
Argument('title'),
MultiKeywordArgument('kw', required=False),
blocks=[('endpanel', 'nodelist')],
)
def get_panel_context(self, arguments):
kw = arguments.pop('kw')
arguments['state'] = kw.get('state', 'default')
return arguments
register.tag(ExamplePanel)
Here's the template:
# panel.html
<div class="panel panel-{{ state }}">
<div class="panel-heading">
<h3 class="panel-title">{{ title }}</h3>
</div>
<div class="panel-body">
{{ body }}
</div>
</div>
And here's how to use it:
{% panel "First Title" state="primary" %}
<p>Hello World!</p>
{% endpanel %}
{% panel "Another Title" %}
<p>Hello World!</p>
{% endpanel %}
This can be done easily using Jinja2 Macros. Hopefully, Django 1.8 will bring native Jinja2 support.
Upvotes: 0
Reputation: 234
I believe the answer you are looking for is written in the documentation, https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#passing-template-variables-to-the-tag
Go down to the section of simple tags and it details how to pass vairable the way you want to
@register.simple_tag
def my_tag(a, b, *args, **kwargs):
warning = kwargs['warning']
profile = kwargs['profile']
...
return ...
Then in the template any number of arguments, separated by spaces, may be passed to the template tag. Like in Python, the values for keyword arguments are set using the equal sign (“=”) and must be provided after the positional arguments. For example:
{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
Upvotes: 3