djc / askama

Type-safe, compiled Jinja-like templates for Rust
Apache License 2.0
3.26k stars 212 forks source link

Macro as parameter for another macro #417

Open cschmatzler opened 3 years ago

cschmatzler commented 3 years ago

Hi,

I am trying to create some components as macros, but I cannot figure out how to use a component inside a component. Let me show you what I am trying to do with an example:

navigation_item.html:

{% macro create(href, icon, text) %}

<a href="{{ href }}">
    <!-- I want an icon here -->
    {{ text }}
</a>

{% endmacro %}

I also have macros for svg icons, such as

{% macro create(class) %}

<svg class="{{ class }}" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>

{% endmacro %}

Now, in my navigation template, how would I insert a specific icon into the navigation item? What I'd like to do is {% call navigation_item::create("/", call icon::create("..."), "Home") %}.

I have also tried passing the icon name as string and then doing {% import "components/icons/{{ icon }}.html" as icon} inside the navigation_item::create macro, but that does not work.

Is there any way to accomplish this?

vallentin commented 3 years ago

It's not possible currently to call a macro like that, e.g. {% call foo(call bar()) %}.

If the icon always results in {% call icon::create(icon) %}, then you can move that part into your initial macro, i.e.:

{% macro link(href, icon, text) %}
    {% call icon(icon) %}
{% endmacro %}

{% macro icon(class) %}
    ...
{% endmacro %}

{% call link("foo", "bar", "baz") %}

Another alternative could be to turn the icon macro into a function, e.g.:

#[derive(Template)]
#[template(path = "foo.html")]
struct FooTemplate {}

fn foo(class: &str) -> String {
    class.to_string()
}

Then you can keep the initial macro as is, and use it like this:

{% call link("foo", self::foo("bar"), "baz") %}