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

Look at template fragments #82

Closed
enthus1ast opened this issue Sep 14, 2022 · 17 comments
Closed

Look at template fragments #82

enthus1ast opened this issue Sep 14, 2022 · 17 comments

Comments

@enthus1ast
Copy link
Owner

https://htmx.org/essays/template-fragments/

@greenersharp
Copy link

Any plans to add fragments to Nimja ?

@enthus1ast
Copy link
Owner Author

I think you can use the self variable for this:
https://github.com/enthus1ast/nimja?tab=readme-ov-file#self-variable

@greenersharp
Copy link

Circling back to this, is there any chance you can show an example of how Nimja can compile a partial template?
I read the docs but I still can't figure out how to render just a partial/fragment of a larger Nimja template.

I am currently using Python for this, with the flask partial library, but I would love to move everything over to Nim/Nimja if possible.

Thank you!

@enthus1ast
Copy link
Owner Author

@greenersharp can you show an example what you want to archive?

Maybe even with code from your jinja2 code.

@enthus1ast enthus1ast reopened this Sep 12, 2024
@enthus1ast
Copy link
Owner Author

enthus1ast commented Sep 12, 2024

When i look at the fragment library i think you can do the same with nimja.

You currently cannot bind a context to importnimja (you can with compileTemplateFile).
But you can just define new variables in a scope (which is like a nim block).
And since child templates have access to the outer variables (in fact the same scoping rules like in nim apply)
this should just do it.

So for example:

foo
{% for entry in entries %}
  {% scope %}
    {% let user = entry.user %}
    {% let another = entry.another %}
    {% importnimja "templates/partials/_oneUser.nimja" %}
  {% end %}
{% endfor %}
baa

The benefit of using scope is, that you do not pollute the outer template with vars.
In the above example it would not make much sense, because the iteration body leaves scope any how.
But in general using scope works good for this and i use it quite often.

_oneUser.nimja

{# renders one user #}
{{ user.name }}
{{ another }}

And if you want to create a "one user" page:

oneUser.nimja

{% extends some/base.nimja %}
{% block content %}
    {# get the user somehow #}
    {% let user = getUserFromDb() %}
    {% let another = getOtherFromDb() %}
    {% importnimja "templates/partials/_oneUser.nimja" %}
{% endblock %}

Is this what you need?

@greenersharp
Copy link

Thanks for the reply!

Here is the python library I was referring to:
https://github.com/sponsfreixes/jinja2-fragments

It's commonly used alongside HTMX to render small pieces of a larger template.
The link above gives a simple example of using the render_block method to render one small piece of a larger template.

It really helps cut down the number of template files in a project.

@enthus1ast
Copy link
Owner Author

@greenersharp have you seen my other message?
Is that what you need?

@enthus1ast
Copy link
Owner Author

ah i see:
return render_block("page.html.jinja2", "content", magic_number=42)

yeah i think this could actually be nice.

@greenersharp
Copy link

Awesome! The feature would pair really well with HTMX.

@enthus1ast
Copy link
Owner Author

enthus1ast commented Sep 15, 2024

I am not 100% sure how it should behave though, but i'll have a closer look.

enthus1ast pushed a commit that referenced this issue Sep 15, 2024
Signed-off-by: David Krause (enthus1ast) <krause@biochem2.uni-frankfurt.de>
@enthus1ast
Copy link
Owner Author

enthus1ast commented Sep 15, 2024

hey @greenersharp if you like you can test the change.

edit:
templateFragment branch
https://github.com/enthus1ast/nimja/tree/templateFragment

I'm still in the process of writing tests for it ( https://github.com/enthus1ast/nimja/blob/d9b7682d91e021608c5353392849d16538dd48ac/tests/basic/test_fragments.nim )

the usage:

you call compileTemplateStr or compileTemplateFile like normal,
but now you can specify blockToRender then it should just render this one block.

    proc foo(blockToRender: static string): string =
      compileTemplateStr("""
        BUG
        {% block first %}first block{% endblock %}
        {% block second %}second block{% endblock %}
        BUG
      """, blockToRender = blockToRender)

    check "first block" == foo("first")
    check "second block" == foo("second")

@enthus1ast
Copy link
Owner Author

Ah btw, if it works for you, are you interested to write an "real world" example for this? For the example folder?
Maybe even with htmx (if you can condense the stuff in a small self contained example).

enthus1ast pushed a commit that referenced this issue Sep 16, 2024
Signed-off-by: David Krause (enthus1ast) <krause@biochem2.uni-frankfurt.de>
@greenersharp
Copy link

Great, thank you so much, I'll definitely try and get a simple working example going

@enthus1ast
Copy link
Owner Author

While doing this PR i also played with htmx (for the first time). And i like it.

@greenersharp
Copy link

greenersharp commented Sep 21, 2024 via email

@greenersharp
Copy link

It works amazingly well. Thank you so much for adding this feature!

I'm new to nim and still learning, but does 'blocktorender' need to be a static string? it would be great if it could be mutable if possible.

Imagine a use case where the rendered block (if any) is conditional on a url query parameter, something like this:

let url = request.uri.parseUrl
var block = ""
if "block" in url.query:
    block = url.query["block"]
request.respond(200, headers, tmplf("index.nimja",baseDir = getScriptDir(),blocktorender=block))

This way I only need to write request.respond once, and I either leave blocktorender empty if I want the whole page rendered, or I set it to a string if I need a partial.

Right now my code looks like this:

if block:
   request.respond(200, headers, tmplf("index.nimja",baseDir = getScriptDir(),blocktorender=block))
else:
   request.respond(200, headers, tmplf("index.nimja",baseDir = getScriptDir()))

Thank you

@enthus1ast
Copy link
Owner Author

enthus1ast commented Sep 23, 2024

Yes it's a static string, I don't think there is a way around this unfortunately. It needs to compile a fresh proc for you when you change the parameters. Then on runtime it's fast.

Nevertheless, I'll look into this problem.

I can imagine that a case statement could work, where you list all the routes explicitly.

Edit: and/or you could create a macro or template, that create the different routes for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants