-
-
Notifications
You must be signed in to change notification settings - Fork 289
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
proposal: support HTML style component authoring #663
Comments
I think we did talk a bit about this syntax when we were designing block components. I think we opted for the current syntax because it is fewer mental jumps when thinking about it. In this syntax would we support the existing syntax or would this replace it? |
I think it could be a little confusing that the attributes in the struct are StartCased and we would use them lowercased in the proposed syntax. Maybe this would be less confusing?
Also, how much work would it take to make the LSP correctly suggest |
Just had a look out of curiosity, and webcomponents do not support In terms of prop names, could be a use for field tags, but LSP might be difficult:
|
+1 to this idea.
So I changed to something like this to avoid the
Being able to write like below would feel much better
|
I would vote against this idea. I believe that the ViewModel abstraction and it's implementation in the current state are very well thought and give you exactly what you'd expect as a resulting component, coming from go. You get the exact behavior you want with little overhead. Something like this...
... feels very natural to me. In one of my projects we define a ViewModel for a FormPage(.templ) for example with multiple inputs and just iterate over the configured inputs. This way you'd only have to call your component once and you can define a primary entrance point to InputComponent behavior. This also makes working with templ a breeze, since you can create and test the ViewModel inside of a go file. This way the LoginPage can just have some styling and a FormPage inside. Maybe I haven't understood completely what's the benefit here 😅 |
FWIW here's another data point. I have no opinions on the View Model component declaration side of things, but would certainly advocate for bringing the component usage closer to HTML.
IMO the competition of any HTML templating language is not Go, its HTML. At my work we have an in-house (React) variant & constraint based design system, and just skimming through a random page in dev tools, I can see that 90%+ elements are individually wrapped in a React component (e.g. In a system like that, which I believe are increasingly common, you really need a JSX like system, that reduces the friction of switching from native DOM elements to a higher order constructs atop them to almost zero. I think it would be fantastic if Templ could achieve something similar. |
Just to clarify the intent, would the idea be that you could create a struct like the one defined and use it like?
If the goal is to help avoid the boilerplate of passing in the necessary state to the subsequent child templates, I don't see how this would solve for that. The caller would still likely need to pass in the data that would end up as arguments to the underly component.
I gather you could instantiate a component and pass that to the renderer, in which case it would be available as a web component.
I can see this being beneficial because it might be easier for a library to return something in a more reusable format without forcing the caller to pass everything. For example, if I had a component defined that allowed adjusting classes used, I could import the pkg, adjust the css/classes and pass that in without having to change the arguments to my entrypoint component. Offhand, I'm not sure about the HTML syntax, but I do see the benefit of adding instances of structs generated at runtime that can be used. My comment here is to primarily to clarify some details for myself, and hopefully others. |
I thought about this a lot the last couple of days. I researched a lot of component libraries (like shoelace.style which joined font-awesome, or microsofts FAST ui) and thus I might be biased to writing @a-h just as a stupid question from somebody that is not deep in parsing: Do you think it would be possible to allow for the Maybe this is something we shouldn't do? I'm just curious. |
Maybe another point to consider: How to set a default value if a field is not set. In the current version, if I have a templ component like this:
In the above, there is a lot of parameters. It's hard to see what's what when it's called.
Also the last two parameters, I'd like to have them as default values. i.e. With this and #713 , there can be a nice pattern for this? |
@jackielii for your case I would probably use #713 to assign defaults to a struct: type textFieldProps struct {
id, name, label, value, err string
span int
disabled bool
}
templ textField(props textFieldProps) {
{{ props.span = cmp.Or(props.span, 3) }}
<div class={ fmt.Sprintf("sm:col-span-%d", props.span) }>
<label for={ id } class="block text-sm font-medium leading-6 text-gray-900">{ props.label }</label>
<div class="mt-2">
<input type="text" name={ props.name } id={ props.id } value={ props.value } autocomplete={ props.name } disabled?={ props.disabled } class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500 disabled:ring-gray-200"/>
</div>
@inputError(props.name, props.err)
</div>
} @textField(textField{ id: "name", name: "name", label: "Name", value: name, err: errs["name"] }) |
Methods on the struct could also work. type TextFieldProps struct {
id, name, label, value, err string
span int
disabled bool
}
func (tfp TextFieldProps) ID() string {
return tfp.id
}
func (tfp TextFieldProps) Name() string {
return tfp.name
}
func (tfp TextFieldProps) Span() int {
if tfp.span > 0 {
return tfp.span
}
return 3
} However, you have to remember to reference the method on the struct, and not access the internal variable. |
I like this proposal and I think it would be beneficial for component reusability, especially when combined with something like Tailwind. However, I do not like React's approach with props when it comes to passing If possible, I would suggest something like Vue's fallthrough attributes where A custom button component styled like this
Could then be styled differently with tiny adjustments
Resulting in rendered HTML with merged
Now, I'm sure that Vue team faced the same issue and that is what happens when you have multiple
...sooo maybe limit the scope and make it work with a single root component first? |
@nikolagava Implicit behavior such as attribute fallthrough is not something I'm personally a fan of, especially if it has conditions such as requiring single root elements. The fact that Vue offers the option to disable attribute inheritance goes to show that it perhaps was not such a good design to begin with, as it clearly divided the community, forcing them to add configuration to disable it. The most important point is once you add something, you can basically never take it away, and many features in JavaScript suffers from this already. I like that Templ and Go in general is very hesitant to changes that are not unanimously decided already. FYI, your example with the merged class doesn't work in practice: templ MyButton() {
<button type="button" class="border rounded px-2 py-1">{ children...}</button>
} with: templ IndexPage() {
<base.MyButton id="btn1" class="p-3">Submit</base.MyButton>
} into -> <button type="button" class="border rounded px-2 py-1 p-3" id="btn1">
Submit
</button> Does not work, as |
Fascinating how much of the web is being abstracted. I didn't even know there were tools like tailwind-merge (or that there was a need for them). But alas, I didn't want to sidetrack this discussion. The point I wanted to make was that I would like to reason about templ components the same way you do regular html elements. And use them like so (albeit a bit naively).
Agree, and I would like to see this discussion turn into something more as I do believe this feature would greatly improve my dev experience working with templ. And possibly even completely remove my reliance on component based frontend frameworks if templ can bridge that gap. |
Would templ benefit from a similar concept to React of props where, if you have a single argument to a React component, you can use a HTML style to use it?
i.e. given the following TypeScript:
You can use this style in JSX:
In templ, it might look like:
You could then write this:
The struct fields would be populated with attribute values using reflection at generation time to match the attributes to field names. The LSP would warn about unknown fields.
The text was updated successfully, but these errors were encountered: