-
Notifications
You must be signed in to change notification settings - Fork 11.2k
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
[8.x] Pass component children to Blade components #36472
Conversation
I've written some very ugly code to make this happen. Framework support would be amazing! I would like to see an API for accessing children from the parent component class. I originally was using a pattern like this for blade-alpine-instantsearch and in that case I really just wanted to access child components for configuration purposes (I eventually moved all the parent/child logic into Alpine.js for various reasons). Another interesting thing would be the ability to access the parent from the child, which actually might be a more flexible solution: class Table extends Component
{
public array $columns = [];
public function addColumn($name, $attribute) {
{
$this->columns[$attribute] = $name;
}
// ...
}
class Column extends Component
{
public function __construct($name, $attribute)
{
$this->parent(Table::class)->addColumn($name, $attribute);
}
public function render()
{
return '';
}
} Sorry, I don't mean to muddy the water! I've definitely been wanting parent/child interaction in Blade components, so I'm just excited to imagine the possibilities! |
@mpociot your example is actually already possible like so: <x-table :data="[['id' => 1, 'name' => 'Marcel'], ['id' => 2, 'name' => 'Adam']]">
{{ $component->addColumn('ID', 'id') }}
{{ $component->addColumn('Name', 'name') }}
</x-table> And the following component: <?php
namespace App\View\Components;
use Illuminate\View\Component;
class Table extends Component
{
public $columns = [];
public $data = [];
/**
* Create a new component instance.
*
* @return void
*/
public function __construct(array $data)
{
$this->data = $data;
}
/**
* Add a column to the table.
*
* @param string $name
* @param array $attributes
*/
public function addColumn($name, $attribute)
{
$this->columns[] = compact('name', 'attribute');
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|string
*/
public function render()
{
return fn () => view('components.table', [
'columns' => $this->columns,
'data' => $this->data
])->render();
}
} |
I think the ergonomics of being able to use the XML-style syntax is really really nice. @mpociot's original example with As an alternative approach, would you be open to an API that exposes |
@inxilpro yeah - i guess it's just preference at that point. Of course, if something is already possible using relatively simple syntax I would always lean towards not taking on any new code to maintain. I actually find both approaches a little odd... I would have just passed the columns as a prop like the data. |
Here's my use-case: Algolia provides some really nice javascript APIs for searching data indexed with Laravel Scout. They have to be configured with JS because of the nature of instant search, but also interact with multiple layers of DOM nodes. Imagine something like this: <x-instantsearch :app-id="$algoliaId" :api-key="$algoliaKey" search-index="tee-shirts">
<x-row>
<x-column width="1/3">
<x-instantsearch::refinement-list attribute="size" />
<x-instantsearch::refinement-list attribute="color" />
</x-column>
<x-column width="2/3">
<x-instantsearch::search-box />
<x-instantsearch::hits />
</x-column>
</x-row>
<x-instantsearch::pagination />
</x-instantsearch> In this case, the final instant search configuration script needs to be set up just once in the parent element, but it needs to know about its children. At the same time, the child nodes need to add HTML to the output. The resulting output should look something like: <div id="instantsearch-container">
<div class="flex">
<div class="w-1/3">
<div id="instantsearch-refinements-size"></div>
<div id="instantsearch-refinements-color"></div>
</div>
<div class="w-2/3">
<div id="instantsearch-search-box"></div>
<div id="instantsearch-hits"></div>
</div>
</div>
<div id="instantsearch-pagination"></div>
</div>
<script>
const search = instantsearch({
indexName: 'tee-shirts',
searchClient: algoliasearch(
'appid',
'apikey'
),
});
search.addWidgets([
instantsearch.widgets.refinementList({ container: '#instantsearch-refinements-size' }),
instantsearch.widgets.refinementList({ container: '#instantsearch-refinements-color' }),
instantsearch.widgets.searchBox({ container: '#instantsearch-search-box' }),
instantsearch.widgets.hits({ container: '#instantsearch-hits' }),
instantsearch.widgets.pagination({ container: '#instantsearch-pagination' }),
]);
</script> We need to be able to centralize the To achieve this, we'd need either:
I think option 2 introduces a little less complexity, because I think it can be achieved by exposing a Does that help demonstrate the value any more? |
@inxilpro does this PR as it is written right now support that exact syntax and use case? |
I'm actually not sure that it does. I didn't want to open a PR if you don't see a need, but I can try to write up some tests and a PR tonight or tomorrow. |
This PR allows Blade components to access their own child elements. This can be extremely useful if you want to build more complex components, or if you want to use components as structured meta-data.
The child components will be accessible in a
__children
variable within your Blade components.For example, you could create a
table
component, that acceptstable-columns
components, which will be used to determine the columns that should be rendered:The (simplified) components could look like this:
The
table-column
component does not require any actual view content, as it is only used to hold the meta information.In order to pass the actual child component instances to the view, this new feature only works with class-based components (either AnonymousComponent or a custom component class). This means, that it won't work when using the
@component
syntax, which is no longer documented anyway.I'm having a hard time figuring out whats the best way to properly test this, that's why this PR does not include automated tests yet.