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

Reusable macros #40

Closed
ahall opened this issue Sep 23, 2014 · 23 comments
Closed

Reusable macros #40

ahall opened this issue Sep 23, 2014 · 23 comments
Assignees

Comments

@ahall
Copy link
Contributor

ahall commented Sep 23, 2014

in jinja2 you can do something like this:

{% from "helpers/macros.html" import my_reusable_macro, my_reusable_macro2 %}

{{ my_reusable_macro() }}

Would it make sense to implement so macros can be imported and reused instead of them having to be in the same template file as they're used in?

@flosch
Copy link
Owner

flosch commented Sep 23, 2014

Sounds like an idea, thanks for your suggestion. I will think about it.

@digitalcrab
Copy link
Contributor

{% from "helpers/macros.html" import my_reusable_macro as new_name1, my_reusable_macro2 as new_name2 %}

or

{% from "helpers/macros.html" import my_reusable_macro = new_name1, my_reusable_macro2 = new_name2 %}

Please add as or = statement

@digitalcrab
Copy link
Contributor

Or even load all macros from specified file (without any names)

@ahall
Copy link
Contributor Author

ahall commented Sep 23, 2014

Yeah I find this takes macros to a whole new level. In current state its not much use for me, but once they can be reused across from multiple templates it'll become very useful for me.

@flosch
Copy link
Owner

flosch commented Sep 23, 2014

Yes, it'll make its way into pongo2 somehow. Will have some time at the end of next week to think about it deeper.

@ahall
Copy link
Contributor Author

ahall commented Sep 26, 2014

One difference from jinja2 I noticed is that in jinja2 you can use keyword args when calling macros (pongo2 lets you define the macros with keyword args). Would that be hard to implement?

@flosch
Copy link
Owner

flosch commented Sep 26, 2014

It would require some fundamental changes in the variable resolver of pongo2 and would lead to a more complex (and time consuming) resolving process. Not sure whether it's worth it.

@ahall
Copy link
Contributor Author

ahall commented Sep 26, 2014

As usual, brilliant explanation, thanks for this.

@digitalcrab
Copy link
Contributor

So this feature will not be implemented?

@flosch
Copy link
Owner

flosch commented Sep 29, 2014

I have plans to implement macro imports in pongo2, my last answer was w.r.t. the last question.

@flosch
Copy link
Owner

flosch commented Oct 1, 2014

I implement the following syntax for macro imports:

{% import "my-other-template.html" my_macro, my_second_macro as renamed_macro %}

Will be available soon. You can't override macros (specifically the macro name) by providing another key through the Context (will lead to an error when calling one of the Execute*-functions).

@ahall
Copy link
Contributor Author

ahall commented Oct 1, 2014

Sounds good, can't wait to test it.

@flosch flosch closed this as completed in 1db32e0 Oct 1, 2014
@flosch
Copy link
Owner

flosch commented Oct 1, 2014

Please test it. See the commit message on how to use it and what changed.

Let me know if it works for you.

@ahall
Copy link
Contributor Author

ahall commented Oct 1, 2014

Brilliant, only found one issue when calling another macros from the macro.

I got a macro file _form.html:

{% import "macros/form.html" render_input, render_input_csrf, render_submit_reset %}

{% macro render_user_form(action, form, selRoles, userId = 0) export %}
    <form method="POST" action="{{ action }}" class="form-horizontal" role="form">
        <fieldset>
            {{ render_input_csrf() }}

            {% if userId != 0 %}
            <input name="id" type="hidden" value="{{ userId }}" />
            {% endif %}

            {{ render_input("fullname", form.Fullname, "Full name", "icon-circle", "text", true) }}
            {{ render_input("username", form.Username, "Username", "icon-circle", "text", true) }}
            {{ render_input("email", form.Email, "Email", "icon-envelope", "text", true) }}
            {{ render_input("password", "", "Password", "icon-lock", "password")|safe }}
            {{ render_input("password_confirm", "", "Password Again", "icon-retweet", "password") }}

            {{ render_submit_reset() }}

            <select name="role_ids" multiple="">
                {% for selRole in selRoles %}
                <option value="{{ selRole.Value }}"{% if selRole.Selected %} selected="selected"{% endif %}>{{ selRole.Label }}</option>
                {% endfor %}
            </select>
        </fieldset>
    </form>
{% endmacro %}

I've tried moving the import statement into the macro itself and that blows things up.

@flosch
Copy link
Owner

flosch commented Oct 1, 2014

Which error message do you get and what does the calling chain looks like (which macro calls which macro from which file)?

@flosch
Copy link
Owner

flosch commented Oct 1, 2014

Try moving the import statement into the macro (nothing else gets evaluated except for the macro-block when importing a macro).

@ahall
Copy link
Contributor Author

ahall commented Oct 2, 2014

macros/form.html

{% macro render_submit_reset() export %}
    <div class="clearfix form-actions">
        <div class="col-md-offset-3 col-md-9">
            <button class="btn btn-info" type="submit">
                <i class="icon-ok bigger-110"></i>
                Submit
            </button>

            <button style="margin-left:0.5em;" class="btn" type="reset">
                <i class="icon-undo bigger-110"></i>
                Reset
            </button>
        </div>
    </div>
{% endmacro %}

{% macro render_input(name, value, placeholder, icon, type, required=false, loginInput=false) export %}
    {% set hasError = State.HasFieldError(name) %}

    <div class="form-group{% if hasError %} has-error{% endif %}">
        <div{% if not loginInput %} class="col-sm-6"{% endif %}>
            <label class="block clearfix">
                <span class="block input-icon input-icon-right">
                    <input name="{{ name }}" type="{{ type }}" class="form-control" placeholder="{{ placeholder }}" value="{{ value }}"{% if required %} required="required"{% endif %} />
                    <i class="{{ icon }}"></i>

                    {% if hasError %}
                    <span class="help-block error">{{ State.FieldErrorStr(name) }}</span>
                    {% endif %}
                </span>
            </label>
        </div>
    </div>
{% endmacro %}

{% macro render_input_csrf() export %}
    <input name="_csrf" type="hidden" value="{{ State.CsrfToken() }}" />
{% endmacro %}

user/_form.html

{% macro render_user_form(action, form, selRoles, userId = 0) export %}
    {% import "macros/form.html" render_input, render_input_csrf, render_submit_reset %}

    <form method="POST" action="{{ action }}" class="form-horizontal" role="form">
        <fieldset>
            {{ render_input_csrf() }}

            {% if userId != 0 %}
            <input name="id" type="hidden" value="{{ userId }}" />
            {% endif %}

            {{ render_input("fullname", form.Fullname, "Full name", "icon-circle", "text", true) }}
            {{ render_input("username", form.Username, "Username", "icon-circle", "text", true) }}
            {{ render_input("email", form.Email, "Email", "icon-envelope", "text", true) }}
            {{ render_input("password", "", "Password", "icon-lock", "password")|safe }}
            {{ render_input("password_confirm", "", "Password Again", "icon-retweet", "password") }}

            {{ render_submit_reset() }}

            <select name="role_ids" multiple="">
                {% for selRole in selRoles %}
                <option value="{{ selRole.Value }}"{% if selRole.Selected %} selected="selected"{% endif %}>{{ selRole.Label }}</option>
                {% endfor %}
            </select>
        </fieldset>
    </form>
{% endmacro %}

This gives bunch of errors such as:

[Execution Error in /tmp/templates/macros/form.html | Line 1 Col 4 ()] Macro 'render_submit_reset' called with too many arguments (6 instead of 0). [Execution Error in /tmp/templates/macros/form.html | Line 1 Col 4 ()] Macro 'render_submit_reset' called with too many arguments (6 instead of 0). [Execution Error in /tmp/templates/macros/form.html | Line 1 Col 4 ()] Macro 'render_submit_reset' called with too many arguments (6 instead of 0). [Execution Error in /tmp/templates/macros/form.html | Line 1 Col 4 ()] Macro 'render_submit_reset' called with too many arguments (5 instead of 0). [Execution Error in /tmp/templates/macros/form.html | Line 1 Col 4 ()] Macro 'render_submit_reset' called with too many arguments (5 instead of 0).
 Submit

It seems like the import statement inside the macro is executing the macros.

@ahall
Copy link
Contributor Author

ahall commented Oct 2, 2014

No, that's what my example here is doing, and gives bunch of errors.

@flosch
Copy link
Owner

flosch commented Oct 2, 2014

Can you please show me the error when you're not importing the macros inside the macro as well (like in your first post)? Thanks!

@ahall
Copy link
Contributor Author

ahall commented Oct 2, 2014

In that case there are no errors, pongo2 doesn't find the macros (render_input, render_input_csrf and submit_csrf) and doesn't execute them. Only thing my macro then yields is the form, fieldset and the select box.

@flosch
Copy link
Owner

flosch commented Oct 2, 2014

Okay, thanks for your feedback. I have to investigate that and will create a new issue for it.

@ahall
Copy link
Contributor Author

ahall commented Oct 2, 2014

Thanks, let me know if you need anything from me :)

Repository owner locked and limited conversation to collaborators Oct 2, 2014
@flosch
Copy link
Owner

flosch commented Oct 2, 2014

@fromYukki Same here: Would be cool if you could give me a short feedback whether this works for you as expected.

flosch added a commit that referenced this issue Oct 27, 2014
{% import "my-macros.tpl" my_macro, my_other_macro as renamed_macro %}.

A macro can be exported by adding "export" to the declaration:

{% macro my_macro() export %} ... {% endmacro %}

Exported macros can't be overriden by a user's Context.
Introduced new API-function: AsSafeValue.
Introduced new keyword: "export"
Conflicts:
	tags_macro.go
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants