Skip to content

[RFC][Twig] "Anonymous" components #283

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

Closed
wants to merge 1 commit into from

Conversation

kbond
Copy link
Member

@kbond kbond commented Feb 18, 2022

Q A
Bug fix? no
New feature? yes
Tickets n/a
License MIT

This is pretty rough still but looking for input. This is basically include() but with the attributes system available.

{# path/to/template.html.twig #}
<div{{ attributes }}>{{ prop1 }}</div>


{# render: (attributes *must* be sent as a "sub-key" #}
{{ component('path/to/template.html.twig', { prop1: 'value', { attributes: { class: 'mb-1' } } }) }}

This feature will be much more useful once/if embedded components are added. You'd be able to have anonymous components with slots.

TODO:

  • since we have the convention of component templates living in templates/components/*, I think we should allow:
    {# if no "alert" component class, use "templates/components/alert.html.twig" #}
    {{ component('alert', {message: 'message...'}) }}

@kbond kbond force-pushed the anonymous-components branch from e80cdbc to a6f28a6 Compare February 18, 2022 21:16
@94noni
Copy link
Contributor

94noni commented Feb 21, 2022

seem weird to have direct html, what about something like this?
that way we can have one templates/component/anonymous-components.html.twig defining n components

{# path/to/template.html.twig #}
- <div{{ attributes }}>{{ prop1 }}</div>
+ {% component 'my_component' %}<div{{ attributes }}>{{ prop1 }}</div>{% endcomponent %}


{# render: (attributes *must* be sent as a "sub-key" #}
- {{ component('path/to/template.html.twig', { prop1: 'value', { attributes: { class: 'mb-1' } } }) }}
+ {{ component('my_component', { prop1: 'value', { attributes: { class: 'mb-1' } } }) }} 

@weaverryan
Copy link
Member

This feature will be much more useful once/if embedded components are added. You'd be able to have anonymous components with slots.

I'd love to see a rough example of what this might look like with anonymous components to give more context :)

@kbond kbond mentioned this pull request May 1, 2022
4 tasks
weaverryan added a commit that referenced this pull request May 18, 2022
This PR was squashed before being merged into the 2.x branch.

Discussion
----------

[Twig] Embedded components

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| Tickets       | Fix #300
| License       | MIT

This adds a `{% component name %}` tag to allow rendering components and overriding their _slots_ (by using twig's native block system).

Example:

```twig
{# templates/components/alert.html.twig #}
<div{{ attributes.defaults({class: 'alert alert-'~type}) }}>
    {% block default %}<strong>{{ type }}</strong> {{ message }}{% endblock %}
</div>

{# "embed" in another template: #}
{% component 'alert' with {message: 'embedded message', class: 'extra-class', role: 'alert'} %}
    {% block default %} {# override "default slot (block)" #}
        {{ parent() }} {# call parent "slot" #}
        Custom stuff...
        {{ this.message }} {# access component properties/methods as normal #}
        {{ computed.someMethod }} {# all component features available #}
    {% endblock %}
{% endcomponent %}

{# can still use the function as normal #}
{{ component('alert', {message: 'some message'}) }}
```

More Advanced Example:

```twig
{# templates/components/dialog.html.twig #}
<div{{ attributes }}>
    <h1>{% block title %}Default Title{% endblock %}</h1>
    <p>{% block body %}Default Body{% endblock %}</p>
    <div>{% block footer %}Default Footer{% endblock %}</div>
</div>

{# "embed" in another template: #}
{% component dialog %} {# quotes can optionally be left off the component name #}
    {% block title %}Custom Title{% endblock %}
    {% block body %}Custom Body{% endblock %}
    {% block footer %}Custom Footer{% endblock %}
{% endcomponent %}
```

Some notes:
1. Most code is copied and modified from twig's native `embed` tag
2. I really wanted to have a default slot to avoid the need for overriding the default block (see first example above) but couldn't figure out how to do this or if it's even possible. Maybe someone with a deeper knowledge of twig internals could help?
3. The `PreRender` event is still dispatched like normal but because of the nature of how this feature works, modifying the template via a listener is not supported.
4. Using with LiveComponent's is not supported and throws an exception if attempted. Perhaps some trickery could be added in the future to achieve but I think this feature adds enough value to stand on its own.
5. This with #283 could add more value I think - it would remove the need for simple components to be _backed by_ a component service.

**TODO:**
- [x] Update changelog
- [x] Tests
- [x] Docs
- [x] Clarify nomenclature, _nested_ components vs _embedded_ components

Commits
-------

b114594 [Twig] Embedded components
@kbond
Copy link
Member Author

kbond commented May 19, 2022

I'm shelving this for now. There is currently little benefit to using this over Twig's standard include/embed. Using the attributes feature is the only useful thing but they have to be passed as an attributes key which is a bummer.

If anything, we could explore adding an attributes() Twig function so this feature can be used anywhere.

@kbond kbond closed this May 19, 2022
@kbond kbond mentioned this pull request Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants