The Third Bear

Just Right.

Reusable code snippets in ActionKit templates

egj actionkit

(5/12/2016) UPDATE: We Also Walk Dogs just released a built-in "Make Your Own Templates" feature which makes this a lot cleaner.  Instead of overriding `wrapper.html` with a giant switch-case, you can now just create a new template for each custom snippet and include that template directly.

It's still worth setting up a dedicated "snippets" templateset for this, though -- that will keep your code more organized and easier to reuse across templatesets, and can also be used as the basis for cleanly overriding snippets in particular templatesets or even running A/B tests on them.

For a long time I've been looking for a way to manage reusable Django code snippets in ActionKit templates.  This would be useful for reducing repetitive lines of custom code used across multiple page types (e.g. code to display a video in any signup, survey or petition page that's using a custom youtube_video_url field) as well as making it easier to keep track of multiple templatesets by sharing code between them.  Having a library of snippets would also help with maintenance even when code is only used once -- just like refactoring code into functions, extracting out and naming template blocks clarifies their purpose, separates concerns, and keeps functionality well-organized and easy to find in the future.

It turns out there's a pretty straightforward built-in way to do this.  The trick is that you can use a template from one templateset in another set.  Between this and Django's built-in include-with syntax, we have everything we need to define and use custom snippets or partials.

To set this up, first create a new templateset.  I'll call it "snippets" but this can be anything you want.  This templateset won't ever be used directly by pages.  Instead we'll use its wrapper template as our "snippet library" and will include that template when we want to invoke a partial.  

So, open up the new wrapper.html template in your snippets templateset and delete everything.  In place of the built-in code, just set up an `{% if %}` block that looks at the value of a "snippet" variable in the template context, with one `{% elif %}` for each new snippet you want to create -- basically a big switch statement:

    {% if snippet == "custom_heading" %}
    <div><h3>Take action!</h3></div>
    {% elif snippet == "embedded_video" %}
    {% if page.custom_fields.embedded_video_url %}
    <iframe src="{{ page.custom_fields.embedded_video_url"></iframe>
    {% endif %}
    {% endif %}

Then in your "real" templatesets, you can just include these to use a snippet:

    {% include "snippets/wrapper.html" with snippet="custom_heading" %}

Note that your snippets will have access to the same template context as the templates that include them, so they can make use of `page`, `args`, `user`, and so on in the same way you normally would.

You can also include snippets with extra custom variables in their context.  This can help with reuse when you have multiple code blocks that are similar, but not quite the same, for example identical markup but different text depending on whether you're using a signup or a petition page:

    # in snippets/wrapper.html
    {% elif snippet == "fancy_submit_button" %}
    <div class="fancy-submit-button"><input type="submit" value="{{ submit_button_text }}"></div>

    # in My Template Set/signup.html
    {% include "snippets/wrapper.html" with snippet="fancy_submit_button" submit_button_text="Join us" %}

    # in My Template Set/petition.html
    {% include "snippets/wrapper.html" with snippet="fancy_submit_button" submit_button_text="Add your name" %}