-
-
Notifications
You must be signed in to change notification settings - Fork 364
Introduce a new syntax for twig component #771
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
Introduce a new syntax for twig component #771
Conversation
Nice. Waiting for this since first time of using twig components :D Will it be possible to pass arrays/objects as attributes if the component defines it? |
Very nice! I've also wanted this from the beginning! Some comments/ideas:
|
Hey, thanks for your returns!
Yes of course 😁
This is why I uppercase the first letter because I don't think there are any tags starting by uppercase in HTML
I don't know how to do it, but It can be a really really cool feature! I will look at it!
Yes! super great idea! And this time I know exactly how to do it! 😁 |
I think this could still be a problem if a user also has, say, vue components on the page.
Awesome, a default slot concept in twig has always vexed me! Just so we're on the same page, this is what I mean by default slot: <UserDashboard user="{{ user.id }}" isAdmin graph="linear" primary>
<div>...</div> <!-- this is the default slot content -->
</UserDashboard> |
What if we could do?:
This probably could avoid conflicts? |
Hey all! I made some updated base on the previous comment: 1 - New syntax you now prefix your tag block with <x-userDashboard>
...
<x-userDashboard/>
</x-userDashboard> 2 - You can now prefix your argument with an ':' in order to pass an object or array or anything that Twig should process <x-userDashboard :user='user.id' isAdmin graph='linear' primary>
...
<x-userDashboard/> 3 - You can use a special syntax for block in embedded component <x-userDashboard :user='user.id' isAdmin graph='linear' primary>
<x-block name='actions'>
<x-userActions isAdmin/>
<x-block/>
<x-block name='footer'>
<x-userDashBoardFooter/>
<x-block/>
<x-userDashboard/>
For the default slot I propose to work on this in a next-up PR since we have to make changes out of range of this PR in order to do it 😁 |
This PR should be ready for review. I need to figure out why all these tests are passing locally but not on the CI |
That |
Could you add a test for passing an object? 😃 |
dd79ad8
to
a9f4d9e
Compare
Awesome work!!! bring JSX-like way of writing components to PHP/Twig! I like! Here some random ideas I have to solve this:
What do you think? |
Does this new syntax also support both react and vue components? |
So here is what this PR does:
{% extends 'base.html.twig' %}
<x-userDashboard :user='user.id' isAdmin graph='linear' primary>
...
<x-userDashboard/>
So, of course, this new syntax support React and Vue since React and Vue are Js Framework, and here everything happens on the server in the PHP.
Please don't use the word ugly to express your opinion on a template syntax. Actually, there is no real conflict between React and this PR. Tell me if I am wrong but React uses what they call a virtual DOM. So in React, you add your component on this virtual DOM, and then React compiles everything to pure HTML and adds that to the root you are defined. And all of that happens in the js, and your HTML is not aware of this compilation. Then if you mean the Symfony Ux React and Vue components. There is no conflict your custom tag gonna be parsed in {{ component('userDashboard') }} and you can still use the react_component() function for your react component where you want them to be.
Actually, I am not a big fan,and at first, see in the first comment I proposed this syntax The other reason is now we introduce the slot syntax <x-userDashboard :user='user.id' isAdmin graph='linear' primary>
<x-block name='actions'>
<x-userActions isAdmin/>
<x-block/>
<x-block name='footer'>
<x-userDashBoardFooter/>
<x-block/>
<x-userDashboard/> So if we get back on the first syntax: we also have to find a syntax for slot. I think we gonna use this syntax only in Twig templates. And so even with this syntax it's gonna be clear that it's a TwigComponent. |
Sorry for the offense. 😕
I completely agree!
<UserDashboard>
Content for the default slot
</UserDashboard>
For what it counts, same vote from me |
@WebMamba THANK YOU FOR THIS 😍!!! I have also been wanting this badly, and it's been crawling higher and higher on my list. I've been on vacation (and I still am for a few days), but I can say a few things quickly: A) About naming: since we are not the first people to come up with this idea, it likely makes sense to look at how other systems (e.g Laravel) do their naming - https://laravel.com/docs/10.x/blade#components - for Blade components, if you have a component named
B) About slots, I like the "named" slot (well, you call them "blocks", which likely makes sense) syntax 👍. We definitely need a "default" block syntax, and ideally we would get it all done in one PR. But if needed, it can be added after. Thank you for this wonderful post-vacation gift! |
I still have same opinion that we could do like in php/js - import the component under default or aliased name so then we could have many components with the same name. For example we have material v2 spec components, but v3 is already released, so if we'd add component with the same name I don't know which it would render. |
yes exactly Hey Ryan thanks for your review! Have a nice Easter holiday time 🐰 <x-userDashboard :user='user.id' isAdmin graph='linear' primary> Or maybe you know the reasons why they made this?
yes, but let's keep this PR short, I think this is already hard enough to review. But this I agree an important feature. Laravel already solve this, and I like the way they took: https://laravel.com/docs/10.x/blade#manually-registering-package-components |
Hmm, perhaps you're right. I think the motivation from places like Blade is that this is meant to look like an HTML tag... and HTML tags are case-insensitive (and almost always shown in all lowercase). But then React always uses upper camel case. So I think you're right. Though, if we DO keep it like Upper Camel Case Naming Convention? Related to this topic (but not this PR, sorry - but it's a good time to talk about it) is naming conventions for Twig components. So far, we've done something like this:
So: PHP class: What does everyone thing about (as a convention) using the class name everywhere:
So: PHP class: Thoughts?I don't want to bike shed, just want to see if there is any big consensus one way or another. |
|
I agree with the It's easier to use this convention because we can simply copy and paste from the PHP class name.
|
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.
Just did some initial testing! Hit some rough patches - but seems like minor stuff.
Agree with you, on this point. Is way much nicer to have: if we don't camel case everything I made the change to fit this syntax. It looks really as @kbond described it in his last comment 😁 |
Ping @WebMamba - I've taken your excellent work and extended it a bit. Please see WebMamba#1 for a new (hopefully more powerful, but still pretty simple) implementation. As a BONUS, that implementation adds support for default blocks: <t:alert type="danger">
<p>Watch our for the dangerously cool new Twig component syntax</p>
</t:alert> In this case, the |
What if it would be called |
28fd38b
to
bd4a214
Compare
Yea, React does this. I think Vue uses slots and doesn't have a default name at all (Blade's default "slot" is also nameless - just |
In DOM it's also children or childNodes:) |
What about using "<ux:" as prefix? (To match the Symfony UX project.) Somehow I find "t" a bit confusing. Is it "template", is it "twig"..? What has "t" to do with "component"? |
I like the naming 'content' as well! I think it's a mistake to be to close the React naming since we took a really different path.
Yes this is t for Twig |
Oh I see - you’re looking now at what the component template itself looks like. I’m not sure: so far the special <twig: syntax is used when rendering a component, not inside the component template itself. That doesn’t mean we can’t change that, just mentioning it. And since we are ultimately using blocks everywhere, I kinda like using “block content” in the component template. But I welcome anyone else to disagree! |
You can choose to use Anyway, I don't feel very strongly about it. I'm fine leaving as just a special named block (this also allows the twig template to use a standard |
No you can't. It wouldn't be hard to add that (IF we wanted it). But currently, we only even start looking for |
@weaverryan just added your code about the default block! 😁
Yeap sure I will open a PR for it!
Good catch 😁 I agree with Ryan on this one, 'content' feels more apparent. Maybe we can add an error if someone tries to do <:t block="content"> we can raise an error like block content is reserved world, please choose another block name. 😁 |
Ahh, ok, got it.
Ok, let's go with
Let's save that for a followup PR @WebMamba, could you adjust this PR's description to show the updated syntax and what's possible with default blocks? |
|
voting for t |
Support both? |
To be honest I was thinking about providing a way to allow custom namespaces. Could be interesting when someone builds a CMS or something on top of that and wants to "rebrand" the prefix for their components. |
…nal name (weaverryan) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] Change component naming standard + optional name | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | None | License | MIT Changes the naming convention of components to upper camel case + makes the `name` argument optional (defaults to class name). Components now look like: ```php #[AsTwigComponent] class Alert { public string $type = 'success'; public string $message; } ``` The template is `templates/components/Alert.html.twig` and you render with `{{ component('Alert') }}`. This removes the need for the user to invent & keep track of an arbitrary name, but they *can* still pass a name to `AsTwigComponent`. No BC break on this. Relates to a conversation on #771 (comment) TODO: * [ ] Update MakerBundle to follow this convention Commits ------- 8dc9a7d [TwigComponent] Change component naming standard + optional name
I'd vote for
Definitely this is something we need to think about, but probably in another PR. If some CMS wants to add new components under some sort of a namespace, that should also affect the way the component is used with the simple syntax |
@kbond @weaverryan just updated to have the syntax with the twig prefix 😁 Maybe in a following PR I will make a proposal to support both syntaxes 😇 |
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.
A few super minor last things. Also - could you add an entry in the CHANGELOG for 2.8 for TwigComponent?
There is also a failing test for DeterministicTwigIdCalculatorTest
. This is actually highlighting a problem. With the new setup, TwigEnvironmentConfigurator::configure()
instantiates new ComponentLexer($environment)
, which extends the core Lexer
. The actually causes the Lexer
__construct()
code to run earlier than it would have before. Hopefully this won't be an issue in real apps - as I don't see a way to work around this currently.
Anyways, to fix the test, we probably need to register the test-only get_id_for_test
twig extension inside the Kernel
itself - there is Kernel.php
file in the TwigComponent test directory where we would wire this in properly.
Btw, if it helps, here's the test fix - WebMamba#2 :) |
Great thanks @weaverryan! Everything is green now! 😁 |
c1c5ce9
to
0f14b03
Compare
0f14b03
to
371edad
Compare
Thank you @WebMamba and @giorgiopogliani for the original inspiration! |
Thanks @weaverryan for your big help on this one! Thanks all for your reviews! 😁 |
…ples, few fixes (weaverryan) This PR was merged into the 2.x branch. Discussion ---------- [Site] More Functional Code, 2x Complex Live Components Examples, few fixes | Q | A | ------------- | --- | Bug fix? | yes (with respect to Twig errors) | New feature? | no | Tickets | None | License | MIT Hi! Prepping ux.symfony.com for 2.8 - very excited about this! Join me on a tour: ## 1) Invoice Creator Live Component Demo https://user-images.githubusercontent.com/121003/232886502-7f8da8f5-fe8a-4251-8e92-c2b56895b1ee.mp4 Parent component + collection of child components communicating by emitting events! ## 2) Product form + modal to create a Category https://user-images.githubusercontent.com/121003/232888282-ab12c4a1-8153-4fe5-babf-cbf4a313c47b.mp4 Opens a Bootstrap modal with a child component inside - and closes that modal from a `LiveAction`. ### 3) Much nicer code blocks Before: <img width="609" alt="Screenshot 2023-04-18 at 3 45 34 PM" src="https://user-images.githubusercontent.com/121003/232888991-67cad476-6836-4b10-9eb5-1ec6b43aafc6.png"> After: <img width="604" alt="Screenshot 2023-04-18 at 3 45 41 PM" src="https://user-images.githubusercontent.com/121003/232889021-01efedd9-c062-4072-a120-1b437d342634.png"> * "Copy" button * Link to view the file on GitHub * use statements are collapsed (click to show them) * "Expand code" to show longer code blocks ### 4) Explanations on longer examples Some examples deserve pointing out a few of the more complex parts. Some of the live component demos now do this: <img width="443" alt="Screenshot 2023-04-18 at 3 48 42 PM" src="https://user-images.githubusercontent.com/121003/232889344-3a776683-40a5-4ef9-8fa7-d909343f563a.png"> ### 5) Using a LOT of new features [Smart rendering](https://symfony.com/bundles/ux-live-component/current/index.html#the-smart-re-render-algorithm), [emitting events](https://symfony.com/bundles/ux-live-component/current/index.html#communication-between-components-emitting-events), [browser events](https://symfony.com/bundles/ux-live-component/current/index.html#dispatching-browser-javascript-events), etc, etc - MANY new features are being used on the site now. We're also using (in som e places) the new `<twig:Component>` syntax from #771. Even though editor support (e.g. for auto-completion) doesn't exist yet, I LOVE this. For example, to render a code block: ```html <twig:CodeBlock filename="src/Twig/SearchPackages.php" /> {# or more complex - logic for extracting" a specific Twig block lives in the CodeBlock.php component #} <twig:CodeBlock filename="templates/main/homepage.html.twig" targetTwigBlock="markdown_example" :showTwigExtends="false" :stripExcessHtml="true" /> ``` Thanks everyone for the work, review & testing on a lot of this new stuff. Using the new features on the site has been BLAST. Commits ------- e6edbde [Site] Adding InvoiceCreator live demo + modal demo
…nal name (weaverryan) This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] Change component naming standard + optional name | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | None | License | MIT Changes the naming convention of components to upper camel case + makes the `name` argument optional (defaults to class name). Components now look like: ```php #[AsTwigComponent] class Alert { public string $type = 'success'; public string $message; } ``` The template is `templates/components/Alert.html.twig` and you render with `{{ component('Alert') }}`. This removes the need for the user to invent & keep track of an arbitrary name, but they *can* still pass a name to `AsTwigComponent`. No BC break on this. Relates to a conversation on symfony/ux#771 (comment) TODO: * [ ] Update MakerBundle to follow this convention Commits ------- 8dc9a7d0 [TwigComponent] Change component naming standard + optional name
Hey all! This PR introduces a new syntax that is more component-oriented 😁
🚨 Just updated this description to feat with the last modification
The problem is: I use components with a lot of parameters, and I use a lot the embedded component feature. So I quickly end up with a hard-to-read template.
So here introduce a new syntax:
instead of:
The old syntax is not removed. This PR gives the ability to choose.
Here is a more realistic example:
{% extends 'admin.html.twig' %} {% block dashboard %} <t:userDashboard :user="user.id" isAdmin graph="linear" primary> <t:block name="actions"> <t:userActions isAdmin/> </t:block> <t:block name="footer"> <t:userDashBoardFooter/> </t:block> </t:userDashboard> {% endblock %}
instead of
So here is a quick summary:
<t:component :user="user.id"> <div> // my content <div> <t:component/>
then in your component template
{% block content %} // your comment will be display here {% endblock %}
Piouffff!!!! A lot of stuff going on here 😎
This PR is inspired a lot by this library: https://github.com/giorgiopogliani/twig-components
Thanks for your time.
Cheers! 😁