I really like templ. I hope it have a great future.
I have made some notes and thoughts while using it which I want to share now - I write it compact, but let's discuss about some points if you want.
1. Pointer Receiver Templates
I commonly use structs as ViewModels
. Instead wire up all of those with it's render function manually this could help a lot:
{% templ (x ViewModel) MyTempl() %}
I used this pattern very much in quicktemplate to make all kind of structures "renderable". Currently in templ I have to create a struct method manually and invoke the render function, for every piece of template.
2. Autocompletion
In this scenario a Component is expected. All functions in the current package, which return a Component are possible candidates.
3. Implicit Package selection
This line of code is most time redundant because the package name can be derrived by the directory name.
When using templ on daily basis I forget this line sometimes.
Also, it is not adjusted by the IDE if the name of the package name changes (i.e. the directory name).
4. Comments
Comment out quickly some code like {% comment %}
in quicktemplate.
What about HTML comment? Or {{/* a comment */}}
5. Alternative START/END-Tag
This point is mabye a matter of taste. On swiss german keyboard, and maybe a lot others, this {%
and %}
is arduous to write. And you do it a lot when writing templates.
Maybe the {{
and }}
which are used in regular go templates is not bad at all.
6. Required Space after START-Tag and before END-Tag
The space after the {%
or before the %}
took some getting used to. Strict on the one hand, prone to error on the other.
Perhaps the parser could point out this error more clearly - or even autofix it (like go fmt for templ).
7. The Context
A really happy that a context I passed arround the Component Render methods.
This way I can access global data or a localizer helper.
But this is undocumented feature and somehow not so nice.
{% if getUser(ctx) != nil %}
Username: {% getUser(ctx).Username %}
Age: {% getLocalizer(ctx).FormatNumber(getUser(ctx).Height) %}
{% endif %}
I have no concrete implementation-idea currently. I make some thoughts...
But in the abstract a shortcut to access a global "struct" variable would do the job.
{% if .user != nil %}
Username: {% .user.Username %}
Age: {% .i18n.FormatNumber(.user.Height) %}
{% endif %}
An easier implementation would be:
A pointer to a struct could be passed via context value, while templ make it visible in a variable x
.
{% if x.user != nil %}
Username: {% x.user.Username %}
Age: {% x.i18n.FormatNumber(x.user.Height) %}
{% endif %}
In both cases the qualified-id must be specified somewhere (for generation and LSP)
Don't get me wrong, I'm not a make it all global type of guy. But it reduces all this argument passtroughts of really important things (i18n, basic user info etc.).
It's just a third source of (typed) data beside receiver variable + argument variables.
8. The Writer
If I'm not mistaken, virtual calls are 5 times slower than direct method calls. And a template calls it thousands of times including the error check.
A benchmark might help.
Maybe it would be interesting to have a look at the implementation of Quicktemplate, where among other things an acquirable byte buffer comes into play.
Another idea would be to be able to define a writer type via CLI argument.
If I manually implement templ components in go, the io.Writer
interface is a bit tedious to work with directly.
9. Switch case improvement
Instead:
{% switch p.Type %}
{% case "test" %}
<span>{%= "Test user" %}</span>
{% endcase %}
{% case "admin" %}
<span>{%= "Admin user" %}</span>
{% endcase %}
{% default %}
<span>{%= "Unknown user" %}</span>
{% enddefault %}
{% endswitch %}
What about:
{% switch p.Type %}
{% case "test" %}
<span>{%= "Test user" %}</span>
{% case "admin" %}
<span>{%= "Admin user" %}</span>
{% default %}
<span>{%= "Unknown user" %}</span>
{% endswitch %}
10. Range improvement
Instead:
{% if len(p.Addresses) == 0 %}
<span>No addresses</span>
{% endif %}
{% for _, v := range p.Addresses %}
<li>{%= v.City %}</li>
{% endfor %}
What about:
{% for _, v := range p.Addresses %}
<li>{%= v.City %}</li>
{% default %}
<span>No addresses</span>
{% endfor %}
This construct is rarely found in template languages. To be honest, I have no idea why.
11. Else-If support
Mabye the switch is a better alternative anyway when it have at least the same capacibilities.
12. Compile a specific file only
Currently recursively all templ
files are compiled.
I configured my IDE or modd to autocompile templ files on every change.