eta-dev / eta

Embedded JS template engine for Node, Deno, and the browser. Lighweight, fast, and pluggable. Written in TypeScript
https://eta.js.org
MIT License
1.41k stars 65 forks source link

Override-able template "blocks" #245

Open jcs224 opened 1 year ago

jcs224 commented 1 year ago

Is your feature request related to a problem? Please describe. There are instances where I'd like to be able to override default content of a template cleanly, such as including different scripts and styles on a different page, or showing different navigation from the default.

Describe the solution you'd like Here is a basic example of what the desired behavior might look like. In this case, overriding a script. Here is the layout:

<!-- main.eta -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Cool New App</title>
    <% block('styles') %>
</head>
<body>
    <h1>Here is some content.</h1>
    <% it.body %>

    <% block('scripts') { %>
    <script>
        console.log('this script will be included on every page by default, unless a sub-template overrides it.')
    </script>
    <% } %>
</body>
</html>

And here is a sub-template, utilizing the above layout:

<!-- some-page.eta -->
<% layout('@main') %>

<p>This is the body of a page.</p>

<% block('styles') { %>
<link rel="stylesheet" href="styles-only-on-this-page.css">
<% } %>

<% block('scripts') { %>
<script>
    console.log('the default script will not load, because I overrode it here.')
</script>
<% } %>

Describe alternatives you've considered I've looked at other JS templating engines, but this fits my needs best based on the runtime flexibility and most of the features I need are available.

Additional context

Here are some existing templating engine's examples that might provide additional context:

https://mozilla.github.io/nunjucks/templating.html#block https://laravel.com/docs/10.x/blade#layouts-using-template-inheritance

nebrelbug commented 1 year ago

@jcs224 this should be possible currently by defining a custom function, block, using the functionHeader option.

jcs224 commented 1 year ago

Awesome, thanks! I looked in the documentation for some help on how to get started with using functionHeader, are there any other examples I can follow? A simple example of an existing implementation would be quite helpful...

nebrelbug commented 1 year ago

@jcs224 I don't have an example right off hand, and am a bit too busy with work to draft one up right now -- sorry :grimacing:.

If you're able to get something working, please comment here and share it with others!

AimForNaN commented 9 months ago

I've checked the source code, and if I read everything correctly, I don't see how this would be possible with the functionHeader option. If you consider the functionHeader option, this requires valid JS code. The syntax proposed in the OP for the custom block function is not valid JS code. Then there is the issue that the layout function is arguably an alias to the include function with the exception that it inserts its own context (for body). This makes it appear that this feature is beyond the scope of everything aforementioned. Arguably, perhaps something can be done using plugins, but I haven't checked to see what AST plugins are exposed to.