开发者

Handlebars.js in Django templates

开发者 https://www.devze.com 2023-03-31 05:42 出处:网络
I need a javascript templating system and i think handlebars.js does an excellent job in this case. I\'m having syntax conflicts with handlebars templates inside a django template because django tries

I need a javascript templating system and i think handlebars.js does an excellent job in this case. I'm having syntax conflicts with handlebars templates inside a django template because django tries to render handlebars variables.

Is there a tag in django templates to stop rendering a block with curly braces?

Something like:

{{ django_context_varable }} #works
{{% raw %}}
<script id="restaurants-tpl" type="text/x-handlebars-template">
    <ul>
    {{#restaurants}} #not rendered by django, plain text
    <li>{{name}}</li>
    {{/restaurants}}
    </ul>
</script>
{{% endraw %}}

Edit

Lik开发者_开发知识库ely i found this. It works fine.

Update

Django 1.5 supports verbatim tag natively.


I use a custom template tag for another js templating system, here: https://gist.github.com/629508

Use in template:

{% load mytags %}
{% verbatim %}
  {{ This won't be touched by {% django's %} template system }}
{% endverbatim %}

Edit: This custom template tag is no longer necessary, as Django's template language now supports the {% verbatim %} template tag.


Is there a tag in django templates to stop rendering a block with curly braces?

OLD Answer for Django 1.0-1.4: No, though you could though you could put the block in a separate file and include it without rendering or use a different templating engine.

New Answer: The answer above was correct in August 2011 when the question was asked and answered. Starting in Django 1.5 (released Feb 2013, though alpha/beta versions in late 2012), they introduced the {% verbatim %} and {% endverbatim %} which will prevent the django template engine from processing the content in the block.

So for the question asked the following will work in django 1.5+ out of the box:

{{ django_context_varable }} #works
{% verbatim %}
<script id="restaurants-tpl" type="text/x-handlebars-template">
    <ul>
    {{#restaurants}} #not rendered by django, plain text
    <li>{{name}}</li>
    {{/restaurants}}
    </ul>
</script>
{% endverbatim %}

The documentation on verbatim is here. Yes, this was noted by others earlier, but as this is the accepted answer I should list the easiest solution.


I wrote a very small django application : django-templatetag-handlebars exactly for that purpose.

{% load templatetag_handlebars %}

{% tplhandlebars "tpl-infos" %}
    {{total}} {% trans "result(s)." %}
    <p>{% trans "Min" %}: {{min}}</p>
    <p>{% trans "Max" %}: {{max}}</p>
{% endtplhandlebars %}

Render your block as usual using Handlebars.js API :

var properties = {
    total: 10,
    min: 5,
    max: 4
};

var template = Handlebars.compile($('#tpl-infos').html()),
    rendered = template(properties);

I wrote it the day @chrisv published its package, with a KISS approach in mind. It is mainly based on Miguel Araujo's gist : https://gist.github.com/893408.


for a deeper integration between handlebars and Django (including optional on-the-fly precompilation) check out my project at https://bitbucket.org/chrisv/django-handlebars/

It basically works like this:

  1. create HB template under

    appdirectory/hbtemplates/myapp/template.html
    

    (just like Django template)

  2. in your app, use

    {% handlebars myapp %} 
    

    template tag and render template like so:

    Handlebars.templates["myapp.template.html"]({context:"value"});
    


Compile your handlebars first!

From handlebars precompiling documentation:

In addition to reducing the download size, eliminating client-side compilation will significantly speed up boot time, as compilation is the most expensive part of Handlebars.

You can compile templates in your build environment using handlebars npm module, or integrate it with a build tool like gulp with gulp-handlebars.

After compiling, your handlebars templates can be served as static resources and bypass server side rendering altogether. Makes it easier on caching too :)

Typical usage would look like this:

<div id="restaurants-tpl">
    Waiting for content...
</div>

<script src="{% static 'js/handlebars.runtime.js' %}"></script>
<script src="{% static 'js/templates.js' %}"></script>
<script>
    // Let django render this as a json string
    properties = {{ properties }};

    // Use Handlebars compiled template imported above
    rendered_html = Handlebars.templates["restaurants-tpl"](properties);

    // Set element content 
    document.getElementById("restaurants-tpl").innerHTLM = rendered_html;
</script>


Django's templating system doesn't support escaping blocks at a time. It would be easy to work around were it not for the fact that when templates are processed the tokenizer doesn't keep exact information on what the tokens looked like before they got tokenized.

I have used the following work-around which is ugly, but (sort of) works. Use different tag delimiters in your templates and a django template tag that translates those back to what you actually want:

@register.tag(name="jstemplate")
def do_jstemplate(parser, token):
    while self.tokens:
        token = self.next_token()
        if token.token_type == TOKEN_BLOCK and token.contents == endtag:
            return
    self.unclosed_block_tag([endtag])
    nodelist = parser.parse( ('endjstemplate',) )
    parser.delete_first_token()
    s = token.split_contents()
    tmpl_id = Variable( s[1] ) if (len(s) == 2 and s[1]) else ''
    return JsTemplateNode( nodelist, tmpl_id )

class JsTemplateNode(template.Node):
    def __init__(self, nodelist, tmpl_id=''):
        self.tmpl_id = tmpl_id
        self.nodelist = nodelist
    def render(self, context):
        content = self.nodelist.render(context)
        return u'<script id="%s" type="text/x-handlebars-template">%s</script>' % (
                self.tmpl_id.resolve(context),
                re.sub( ur'{\$(.*?)\$}', u'{{\\1}}', content ), )

For bonus points you can leverage Django's templates within your templates ... which will probably cook your brain trying to untangle later:

{% jstemplate "restaurants-tpl" %}
{$#restaurants$}
<div id="<$name$<" class="{$type$}">
    <ul class="info">
        {$#if info/price_range$}<li><em>{{ trans "Price Range" }}:</em> {$info/price_range$}</li>{$/if$}
        {$#if info/awards$}<li><em>{{ trans "Awards" }}:</em> {$info/awards$}{$/if$}
    </ul>
    <div class="options">
        <button>{% trans "Reservation" %}</button>
    </div>
</div>
{$/restaurants$}
{% jstemplate %}


Actually I wrote a custom template filter which goes like this:

from django import template
register = template.Library()
def handlebars(value):
    return '{{%s}}' % value
register.filter('handlebars', handlebars)

and use it in a template like so:

{{"this.is.a.handlebars.variable"|handlebars}}

It's the simplest thing I could think of. You just have to put your handlebars variable name in quotes. I regret I hadn't got this idea before I struggled with ssi. It works also with keywords:

{{"#each items"|handlebars}}


Why not use jinja2 instead? IMO, they're both elegant to use. Here's an excellent article about it: Using Jinja2 with Django

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号