Skip to content
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

Multi-word blade component attributes aren't passed to child component #48956

Closed
jacksleight opened this issue Nov 9, 2023 · 5 comments
Closed

Comments

@jacksleight
Copy link

jacksleight commented Nov 9, 2023

Laravel Version

10.31.0

PHP Version

8.2.11

Database Driver & Version

No response

Description

When passing multi-word blade component attributes to a child component they don't make it to the child component. Probably best explained with an example:

Steps To Reproduce

I have a blade component called foo:

@props([
    'one' => 'none',
    'twoWord' => 'none',
])
{{ $one }} {{ $twoWord }}

And I have another blade component called bar which simply wraps foo and passes everything down:

<x-foo :attributes="$attributes"></x-foo>

If I render these two components like this:

<x-foo one="ok" two-word="ok"></x-foo>
<x-bar one="ok" two-word="ok"></x-bar>

I would expect this output:

ok ok
ok ok

However instead I get:

ok ok
ok none

In both instances if I dump the attribute bag inside foo it's empty, which I would expect.

The two-word/$twoWord value just seems to disappear when passed via bar, but the one/$one value works fine.

Screenshot 2023-11-09 at 11 41 06

foo and bar are both anonymous components.

@jacksleight jacksleight changed the title Multi-word blade component attributes aren't passed to child component props Multi-word blade component attributes aren't passed to child component Nov 9, 2023
Copy link

github-actions bot commented Nov 9, 2023

Thank you for reporting this issue!

As Laravel is an open source project, we rely on the community to help us diagnose and fix issues as it is not possible to research and fix every issue reported to us via GitHub.

If possible, please make a pull request fixing the issue you have described, along with corresponding tests. All pull requests are promptly reviewed by the Laravel team.

Thank you!

@guiassemany
Copy link

guiassemany commented Nov 9, 2023

Hello!

I do not have a great amount of experience using Blade components, but I noticed something a while ago that made me realize I might not fully understand all the concepts about Blade components, and perhaps I was the one making mistakes.

Maybe it is a bug, but I'm not sure. I'd prefer to share this here and see if someone with more experience can figure it out.

Let's consider the scenario that @jacksleight mentioned above.

"foo" anonymous Blade component:

// resources/views/components/foo.blade.php
@props([
    'one' => 'none',
    'twoWord' => 'none',
])
<div>
    One: {{ $one }} <br>
    Two: {{ $twoWord }} <br>
</div>

And the "bar" anonymous blade component

// resources/views/components/bar.blade.php
<div>
  <x-foo :attributes="$attributes" />
</div>

If we use them like this on the welcome.blade.php file

foo
<x-foo one="ok" two-word="ok" />

bar (kebab-case):
<x-bar one="ok" two-word="ok" />

bar (camelCase):
<x-bar one="ok" twoWord="ok" />

For some reason, the kebab-case version of the bar component won't work as expected, but the camelCase version will.

This is the output:
CleanShot 2023-11-09 at 14 01 01

To make the kebab-case version work, I had to use @ aware on the foo component and put the name of the prop.

@props([
    'one' => 'none',
    'twoWord' => 'none',
])
@aware(['twoWord'])
<div>
    One: {{ $one }} <br>
    Two: {{ $twoWord }} <br>
</div>

CleanShot 2023-11-09 at 14 35 44

But I think this is not how it's supposed to work.

The docs says this about components:

Component constructor arguments should be specified using camelCase, while kebab-case should be used when referencing the argument names in your HTML attributes. For example, given the following component constructor

Again, there is a good chance that I am misunderstanding something, but I just wanted to contribute this information with the bug the @jacksleight mentioned.

@jacksleight
Copy link
Author

jacksleight commented Nov 9, 2023

Yeah my understanding is that you should always use kebab case in the tag and camel case in @props.

And that does work as expected if you make $twoWords a prop in bar, but something is preventing that from working when the values are passed down to foo from bar.

@taylorotwell
Copy link
Member

If you know it is a prop you should declare it as such in your wrapped component and pass it as a propped to the final child component.

@jacksleight
Copy link
Author

Ah OK, the thing is you wouldn't always know it's a prop.

The idea here is that the bar component is a simple wrapper around a foo component, and will blindly pass everything it receives down to foo while setting one or more props/attrs of it's own. (The example above is simplified obviously, in my actual app I have a button component and a button.loading component. button.loading accepts and passes on everything button accepts, while setting a couple of values in the process.)

While I could define all of foo's props in bar as well, that would create a whole load of duplication, and it gets tricky if foo is a component from a 3rd party package where you wont always know when new props have been added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants