-
-
Notifications
You must be signed in to change notification settings - Fork 288
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
Simplifying the use of Templ components with HTMX #229
Comments
Hey @gungun974, I'm just getting to know Templ, but seems you could separate out the attribute rendering and define your own types for HTMX params... Haven't tested this, but would it work for to do the following?: type HTMXParams struct {
Get string
Target string
Swap string
}
type AppButtonParams struct {
Variant string
Size string
Class string
HTMXParams // Embedded
}
func renderAttributes(attrs map[string]string) string {
var result []string
for key, val := range attrs {
if val != "" {
result = append(result, fmt.Sprintf("%s=\"%s\"", key, val))
}
}
return strings.Join(result, " ")
}
templ AppButton(params AppButtonParams) {
<button
class={
templ.SafeClass(appButtonVariants(params.Variant, params.Size)),
templ.SafeClass(params.Class),
}
{ renderAttributes(map[string]string{
"hx-get": params.Get,
"hx-target": params.Target,
"hx-swap": params.Swap,
}) }
>
{ children... }
</button>
} |
Thanks for the suggestion. After maybe this is something that can be added to Templ. But not in this form. It would be very strange to permit inject raw string into an element like this. A better and simple well would permit to passe in a The Parser will have to recognize this pattern and make the generator generate a simple for range with So this is my suggestion of feature. If you think it's a good idea let me know. |
I was thinking a little bit about this today and I thought something like an element attribute serializer would be kind of nice. Given your example, we could specify to the serializer how our structure should be serialized into the element:
And then in our templ function we could write:
And templ could desugar this into something that invokes an attribute serializer to write the elements into the button position. I haven't looked at the templ code in detail so here would be a naive interpretation:
You might even be able to extend this further to encompass the I think this system could even be used to eliminate the need for
Alternatively to all of this. Templ already supports |
I can see this working, but I usually avoid struct tags like the plague, it would make more sense to me to be a new type that can be used. I don't think it would remove the need for A possibility would be for there to be a new type
|
I really like this syntax because it's solve HTMX attribute but also generic attribute and is simple and clean. I can easily imagine write something like this with your code : @AppButton(templ.Attributes{"data-id": "my-custom-id"}) {
Click
} I also think it's could be very simple to implement since we just need to loop the map and do some edit to the parser. I can create the PR if you want since I really enjoy to but before what should happend if an attribute already exist ?
|
@joerdav Good points. I could see solutions with the tags but as you've said they can become annoying and they aren't in spirit with how the rest of templ looks and functions. Brief review of how CSS worksRendering attributes is closely related to how CSS classes are rendered. So I think it's worth taking a brief look at how it works in templ currently.
You can use I wouldn't necessarily call the CSS API easy to use manually. The documentation doesn't even state
You then must manually place it into the class attribute for your elements:
AttributesI propose that we could offer similar template syntax for attributes as we do for CSS in addition to offering a friendly manual API. Manual Usage
Template Usage
One major benefit of this style is that you free up the I'm not necessarily a fan that it is different syntax within the This would bring it inline with the manual api usage but differ in how the CSS template syntax works:
There is precedence in templ for both usages:
I noticed there is a PR for allowing parameterizing the css which means those functions could take arguments potentially. Perhaps there is a technical reason for allowing both function style and spread style for both CSS and Attributes. If there are no arguments you could simply use the spread style for both CSS and Attributes. This would bring them both inline with each other. Technical considerationsProposed answers to some of the questions brought up by @gungun974
I'm not aware of how browsers handle precedence for attributes. I'm not even sure there is a standard behavior here. For example:
I think this is a similar question to the previous one. Merging should be possible and any overlapping attributes For example:
Again. I think this is similar to the first two questions. The last most specified attribute should be the
I'm not sure what the current templ parser does for inline attributes on elements. Therefore, if the parser does not already parse out the various attributes then I propose Example:
|
Thanks for this suggestion @xgalaxy but I don't understand how your new keyword With the example you show it's like a map of string but it's not really a map cause it's call like a function ??? I think it's maybe too complex and sophisticated for Go and Templ. I think add this syntax will just make Templ more complex to maintain for not a lot of benefits against a regular map that everyone know (or For the CSS templ class stuff I'm not qualify to speak or even understand since I don't use that. (Regular CSS class work great for me) For my question I didn't think first about multiple spread attribute but I think it's a good idea (I said that cause I think it's will be easy to have multiple than only one in the parser) For the merge conflict of attribute override seem to be the more logical solution but for what i see of the go generator code. We just write a plain text of attribute without really know what the previous was. That could impact runtime performance if we need to replace those plain text to a map and do some override with the spread. But this is an implementation detail so I guess I will begin to implement a prototype of this feature in a draft PR. |
The syntax is derived from how templ already does CSS components. I personally don't think its too complex. See: https://templ.guide/syntax-and-usage/css-style-management#css-components |
Yes it's true templ support that but I don't see how this syntax is superior to a regular struct well supported by golang compiler. Templ is a superset of go. We should really a maximum of what golang give to us. And for the Finally I don't think a sugar like |
I've finished creating my draft pull request. It was simpler than I imagined to create the parser given the current project structure. However, as for the generator, I have no real idea how to overwrite an attribute that was previously written since the generator knows neither the past nor the future, and it only writes text. We would need to completely move the attribute system into the runtime when a spread attribute is used, but this is costly and very specific case. If you have technical ideas or suggestion, please say that in PR #237. If you think it's impossible or too costly at runtime, we could skip this kind of verification and revisit it later. |
Agreed, I think it's up to the developer to not write duplicates, and up to the browser to handle the cases where duplicates are present. @xgalaxy I have to agree with @gungun974 here that the |
Sorry, I haven't had a chance to really absorb the comments and thinking on this yet. From a quick look at the PR, I can see that the templ.Attributes is a Doesn't this mean that we can only spread constant attributes, when we might also want to spread Go expression attributes, CSS scripts, etc.? The full set of allowed attributes is at templ/parser/v2/elementparser.go Lines 265 to 282 in faa36c1
|
Other than that, I think I can state the requirement as being that we want a templ component to be able to accept "rest parameters" which are applied to one or more children.
This probably works OK at a single level of nesting, but if I want to pass conditional attributes from a parent to child components, there's no syntax in templ to help, so it would be this monstrosity.
This makes me think that we'd have to update the
This adds a bit of complexity because in this view, the attribute "name" becomes an input to the "Component" function. The LSP could be updated to work that out. All attribute names in the templ component would need to be mapped to function inputs ( |
If I understand correctly what you said @a-h : You'd rather handle dynamic attributes using a slice instead of a map (and then we can utilize some variadic functions). Personally, I believe it's not a good idea to create an array-like structure of If we were to proceed in this manner, within the generator, we would need to implement a loop to check if an element is a
After for this code, the issue is not about a spread operator or passing attribute but rather, templ component are go function and we can't put in a go function parameter a if statement or ternary (RIP ternary) If I wanted to write "hoi" in name if
Finally the idea of using a pure HTML attribute rather than I think for this issue what we need is something more simple and more go idiomatic. If you look into #237 and the doc I wrote :
The idea here is to make templ just spread a simple go map (but we can also change this to a slice). The example you showed before about conditional attribute is possible but using real go code to create a map that correspond of what you want to do. func getAttributes(a int, b int) templ.Attributes {
if a < b {
return templ.Attributes{"title": "name"}
}
return templ.Attributes{}
}
templ Component(name string, attrs templ.Attributes) {
<div { attrs... }>
{ name }
</div>
}
templ Higher(name string, a, b int) {
<div>
@component(name, getAttributes(a, b))
</div>
} |
@a-h I'm also not sure I follow on why you would pass a conditional attribute this way, or a bool expression. If you wanted to conditionally add something this way you wouldn't add it to the map/slice, is what I was thinking. As a parallel, with react props I believe you would reconcile any expressions before passing as props, rather than a conditional that is reconciled every time it's used. I may be missing the point! |
Ah, OK, I think I understand now, and why it makes sense that it's a map! Thanks. |
You should add @gungun974 conditional attribute example as part of the documentation to show people how they can use it with conditionals. |
The example I show is a pattern that templ force me to do. Contrary in JS with JSX, we can't write go code in the rendering function. We can't for example jus't create a variable in the
So if we need to add something in the docs related to this, it's more about this pattern to remember in (But then I want to say it would be simpler and more obvious for people if we could directly insert go code in |
Literally ran into this exact issue when I created my first component a few minutes ago, also with HTMX hx-* attributes! #237 is a great solution as it aligns with the way of many (probably all) existing JS frameworks and that makes it fairly easy to adapt to Templ when coming from JS frameworks. I'd offer help but I am just starting out with Go and am probably more of a blocker right now, however, I really wanted to say thanks to all of you for working on this, it looks great, and can't wait until the PR gets merged! Looking forward to shill Templ even more in the future :) |
Thanks the proposal change of my PR is great. I don't want to rush anyone to merge it but it's true I'm hype to get some news from a-h.
I just want to tell you something about this. Don't believe you are a blocker before even trying ! Okay I write a lot of text, and maybe it's a little strange but it's important as a person to always try and I wanted to share this here ! |
Time to close this now the PR has been merged. Thanks for all the input on this! |
I was working on an HTMX project with Templ and I was wondering if there was a simpler way to use Templ components with HTMX.
In my application, to avoid reinventing my button everywhere, I created a Component like this :
The challenge now is that I want to use an
hx-get
attribute for a specific button. With the current syntax of these components, I can't transparently pass new attributes to myAppButton
component since it's a function.Is the only way to pass props to a Templ component by passing values into the function, or is there a syntax sugar I can use to simplify this code ?
The text was updated successfully, but these errors were encountered: