-
-
Notifications
You must be signed in to change notification settings - Fork 364
Anonymous TwigComponent #802
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
Anonymous TwigComponent #802
Conversation
Love this! (attempted this in #283 a while back but now that we have html-syntax and default slots, it's much more useful). What about component properties/attributes? <twig:button.button class="foo" :username="user.name" :isAdmin>
Click Me!
</twig:button.button> |
Ah, awesome! I'd really like to get this working too! To follow-up on Kevin's comment: We need a way to differentiate a "variable" being passed to the template vs an attribute. Fortunately, we are not the first to face this problem ;) https://laravel.com/docs/10.x/blade#data-properties-attributes - so it may require some new tag on top of the component template - like I'm also not sure, as a convention, to start mixing |
I like this syntax (if it's possible).
If I understand the new html syntax correctly, upper camel case is not required. Assuming that's true, I don't know we can have a universal way to easily migrate from anon to non-anon. I feel like find-and-replace would be the best migration path. I wonder if, to start, we should pass the full template? <twig:path/to/button.html.twig type="info" class="mb-1"/> Or maybe we can drop the <twig:path/to/button type="info" class="mb-1"/> |
If we, as a standard, always used upper camel case - like twig:SuccessButton - even for anonymous components (so, the template would be SuccessButton.html.twig, then we’re good. Simply introducing a SuccessButton component class would be enough. What likely wouldn’t be as clean are anonymous components that live in sub-directories. |
I think this would still be possible with what I propose. For
|
I was thinking about something similar, but I think it's important to let the ability to type checking. So my proposal is something like so: {% prop 'info' is 'string' default 'info' %}
{% prop 'foo' is 'bool' default 'false' %}
This is a really good point! Because in my use the front-end developer first is the template, and then a Symfony developer as the back-end logic. So I think we should let the ability to add the component class without touching the templates.
And why not also look at every subdirectory, and we just force to have every template with a different name under the components directory?
Hum yes, this is interesting, I like this priority check idea! By mixing your comments the solution could be: first, imagine you have the following template: components/Button/Primary.html.twig then in an other template you use like so: <twig:Button.Primary/> and then if you create a new component class: #[AsTwigComponent('Button.Primary')]
class ButtonPrimary
{
} |
That syntax looks complex to me :). My opinion: if you are about types, use a class-backed component. There is nowhere else in Twig (e.g. So, I'd go back to my original proposal (well, I've modified to {% props type='info', message %}
I might remove 2 & 3 (motivation: keep these anonymous components looking/feeling like everything else)? And is 4 really needed - I guess you'd have For 2 & 3, if we want to allow Twig components to live in other paths, probably we could add a different extension point for that - e.g. Blade components allow you to configure a "namespace" of sorts - e.g. a |
I think we should keep the directory separator as
As long as (for an end-app), While it looks weird, I think this is the easiest way to allow including anon. templates from 3rd party bundles: Is the performance of running the chain part of your objection? I do think we should put the best-practice/most-common scenarios highest on the chain. |
I think a lot of people, myself included, would want to use these as super-charged include tags and may not have any intention of using real components. |
Ok fair - we can keep 2 & 3 :) |
I don't want to wake sleeping dogs, but why is the directory separator different from the template namespace? That would mean an anonymous twig component would be written differently than a class component: {# components/button/button.html.twig #}
<button class="btn btn-{{ variant }}">
{% block content %}{% endblock %}
</button>
{# Anonymous component #}
<twig:button.button variant="primary">Click me</twig:button.button>
{# or #}
<twig:button/button variant="primary">Click me</twig:button/button>
{# Class component #}
<twig:button:button variant="primary">Click me</twig:button:button> It would be great if the directory separators would be similar. That way, devs will be able to just remove the component class and the components would still work. |
Hey @AmProsius! Thanks for your comment! I think you are right and this is the direction we gonna took here! I will keep working on this PR this week! 😁 I was just focused on the bug we have with the new twig component syntax 😁 Cheers! |
For this PR you could take help from https://laravel.com/docs/10.x/blade#anonymous-components 😄 |
Hey all! I think we need to work first on what @weaverryan talks about in this comment #844 (comment) before continuing this PR 😁 |
fd2cfa1
to
04b4c30
Compare
Hey all! I am back on this! I updated the first comment if you want some examples, but what I did is:
Tell me what you think about this change! Cheers 🧡 |
04b4c30
to
2bffe7c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, I'm excited! I'm happy you got the prop system working.
now you put your static components in template templates/, templates/.html.twig, templates/component/, templates/component/.html.twig
Does this mean you can use <twig:Form.Button.html.twig>
?
src/TwigComponent/tests/Fixtures/templates/render_static_component.html.twig
Outdated
Show resolved
Hide resolved
src/TwigComponent/tests/Fixtures/templates/anonymous_component.html.twig
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting very close now! I dropped some comments - but we also need documentation!
src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,5 @@ | |||
{% props label, primary = true %} | |||
|
|||
<button class="{{ primary ? 'primary' : 'secondary' }}"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use attributes
here:
<button {{ attributes.defaults({ class: primary ? 'primary' : 'secondary' }) }}>
The reason is that, we need to test that anything that is a prop is NOT added as an attribute (and I think this may not be implemented yet?). So given this example:
<twig:Button label='Click me' :primary='false'/>
We need to assert that, even if we use {{ attributes }}
, both label
and primary
don't show up as attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum, I am not sure we should have a difference between argument and prop. Why do you think we should differentiate theses two notions?
13789f7
to
b19c71a
Compare
@weaverryan for the doc I plan to let a colleague do it, it's her first PR 😁 |
I think it's been decided that |
/** | ||
* @author Matheo Daninos <matheo.daninos@gmail.com> | ||
*/ | ||
final class ComponentTemplateFinder implements ComponentTemplateFinderInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a bit like a premature abstraction, what about in-lining in ComponentFactory
for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@weaverryan ask me to do it in this comment: #802 (comment).
To not have to autowire the Environment service
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, it felt odd to inject ALL of Environment
into ComponentFactory
, which is otherwise unaware of Twig iirc
Hey all! I am back from holiday! And back to making this PR live 😎 @kbond PR description updated 👍 |
Thank you for your hard work @WebMamba! Now, can you take a shot at writing the documentation? |
Woup Woup 🕺🏽 !! So happy to have this merge! 😁🎊🎉 @weaverryan I am on it 😎 |
Are arrays taken into account? Example: <twig:TabbedCodeBlocks :files="[
'src/Twig/MealPlanner.php',
'templates/components/MealPlanner.html.twig',
'src/Form/MealPlannerForm.php',
]" /> I have an error |
I tried to make a simple example <twig:Button label='Click me' :primary='false'/> {# templates/components/Button.html.twig #}
{% props label, primary = true %}
<button {{ attributes.defaults({class: primary ? 'primary' : 'secondary'}) }}>
{{ label }}
</button> The render is as follows but I have <button class="secondary" label="Click me">
Click me
</button> |
thanks @WebMamba |
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Docs] Anonymous twig components | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | Tickets | none | License | MIT Add documentation for the recently added support of anonymous twig components [https://github.com/symfony/ux/pull/802](https://github.com/symfony/ux/pull/802) Commits ------- 41f59f1 [Docs] Anonymous twig components
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Docs] Anonymous twig components | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | Tickets | none | License | MIT Add documentation for the recently added support of anonymous twig components [https://github.com/symfony/ux/pull/802](https://github.com/symfony/ux/pull/802) Commits ------- 41f59f18 [Docs] Anonymous twig components
I would like to know if it is possible to have a dynamic component name. {% set items = [
{icon: 'Home'},
{icon: 'MagnifyingGlass'},
{icon: 'Bell'},
{icon: 'Users'},
{icon: 'Heart'},
] %}
{% for item in items %}
<twig:Icon:{item.icon} class="h-6 w-6" />
{% endfor %} |
Hey all!
TwigComponent getting really cool: a custom syntax, embedded component, nested component, and so on. Why not extends all of this to anonymous components?
What I call an anonymous component is a simple template.
For example if you have the following template:
// {% components/button/button.html.twig %} <button class="primary large actions"> <span> {% block content %} {% endblock %} </span> </button>
You can now use it like so:
As can see you can use
:
to navigate through the directories.And this PR came also with twig_entension configuration key (default to .html.twig), to find your template with the extension you want. 😁
You now have the {% props %} tag. This tag allows you to define required props for a static component or to set a default value.
So here you have one require prop
label
, and one optional prop primary with a default value set to true. You can now use your anonymous component like so: