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

[8.x] Resolve component class with $attributes property #32505

Closed
wants to merge 3 commits into from
Closed

[8.x] Resolve component class with $attributes property #32505

wants to merge 3 commits into from

Conversation

panda-madness
Copy link
Contributor

@panda-madness panda-madness commented Apr 23, 2020

This small change allows one to type hint the $attributes parameter in the class constructor. This parameter contains an array of all attributes that were passed to this component.

<x-test-subject :foo="'bar'" baz="cookies" />
<?php

namespace App\View\Components;

class TestSubject extends Base
{
    public function __construct($attributes)
    {
        dump($attributes); // contains ['foo' => 'bar', 'baz' => 'cookies']
    }
}

As this change reserves the $attribute parameter name it's technically breaking, although it's pretty small, so not sure whether this is major or minor feature.

I will adjust the tests if this PR gets a green light.

This small change allows one to type hint the $attributes parameter in the class constructor. This parameter contains an array of all attributes that were passed to this component.
@GrahamCampbell GrahamCampbell changed the title Resolve component class with $attributes property [8.x] Resolve component class with $attributes property Apr 23, 2020
@GrahamCampbell
Copy link
Member

Thanks for the PR, however the tests are failing.

@panda-madness
Copy link
Contributor Author

@GrahamCampbell I've fixed the tests.
Also wanted to ask if this is something that can be merged in 7.x instead?

@taylorotwell
Copy link
Member

You can access $this->attributes from within the render method.

@panda-madness
Copy link
Contributor Author

panda-madness commented Apr 23, 2020

@taylorotwell here is a reproducible demo showing that that is not the case, $this->attributes is still null even in render. Note the TestSubject component.

Screen Shot 2020-04-23 at 20 49 54

@driesvints
Copy link
Member

@panda-madness you commited your custom changes together with the framework skeleton which makes it super hard to find out what's wrong.

@panda-madness
Copy link
Contributor Author

@driesvints Sorry about that. I've added some changes in a single commit to mark every file I modified, I'll know to separate my changes in the future.

@panda-madness
Copy link
Contributor Author

Bump in the night, @taylorotwell

@driesvints
Copy link
Member

@panda-madness I've actually stumbled onto this myself in the meantime. I'll have a look at this today.

@panda-madness
Copy link
Contributor Author

@driesvints something related to blade svg I guess? :)

@driesvints
Copy link
Member

@panda-madness heh yeah.

I discovered that you can still use attributes in your render method like this actually:

    public function render()
    {
        return <<<'blade'
        <div class="<div {{ $attributes }}">
            Foo
        </div>
blade;
    }

Is there a special reason why you need to access the attributes? It's best that you provide a real-life use case. I'm having a look tomorrow again so let me know.

@panda-madness
Copy link
Contributor Author

panda-madness commented Apr 27, 2020

@driesvints I was trying to make a sort of dynamic component that would render other components and pass along the attributes that it receives, sort of like in Vue. For that I needed to extract 1 defined attribute (the component name), and pass everything else in the template.

I actually made attributes work the way Taylor described at some point while digging this issue, by moving this withAttributes call just after the component object instantiation, specifically here. I didn't know what other side effects that might have entailed, though, so I went with this PR, because I thought this approach was less invasive.

@driesvints
Copy link
Member

@panda-madness I'm looking into doing that exact thing but you have to be aware that $data != $attributes. Data is all the data from the view piped through to components and attributes are the attributes defined in the html tag of the component.

What I would do in your case is dynamically render different views which have these components defined. Then you don't need to define them in your render method.

@driesvints
Copy link
Member

@panda-madness I've looked further into this but was unable to find a clean solution. I think it's a no-go on attributes being available in the render method for now.

@panda-madness
Copy link
Contributor Author

@driesvints would you consider petitioning Taylor to re-open this PR then? I suspect he might have unsubscribed from the discussion here.

@driesvints
Copy link
Member

No, because I don't think this is the right solution either to mix data with attributes.

@panda-madness
Copy link
Contributor Author

@driesvints I might be greatly misunderstanding how component tag compilation works, so please correct me if I'm wrong.

From what I'm seeing, $data initially contains all of the passed HTML attributes, and nothing else. You can see here and here where the attributes are Regex extracted and passed to $this->componentString(). The partitioning afterwards is, I assume, made to specifically enable constructor dependency injection via the service container, since the container needs named arguments for that.

I don't really see where we're mixing anything that's not supposed to be mixed, as far as I can see we are only operating on the attributes that are passed to the component tag.

Again, if I'm mistaken I'm sorry to have taken so much of your time on this.

@driesvints
Copy link
Member

Technically your change will work momentarily I believe but I think it'll collide with changes made in #32576 for the next major release.

@panda-madness
Copy link
Contributor Author

panda-madness commented Apr 30, 2020

Is there a way to trigger the test suite against the new master branch? Apart from creating a new PR.

@driesvints
Copy link
Member

Yeah, run them locally.

@bastinald
Copy link

This should have been merged.

Attributes and slots should be accessible through the class constructor.

Here is a use case example for a component using Livewire:

class Input extends Component
{
    public $model;

    public function __construct()
    {
        $this->model = $this->attributes->whereStartsWith('wire:model')->first();
    }

    public function render()
    {
        return <<<'HTML'
            <div class="mb-4 space-y-1">
                <input {{ 
                    $attributes->class([
                        'w-full placeholder-gray-400 rounded-lg',
                        'border-gray-400' => !$errors->has($model),
                        'border-red-600' => $errors->has($model),
                    ])->merge([
                        'type' => 'text',
                    ])
                }}>

                @error($model)
                    <p class="text-sm text-red-600">{{ $message }}</p>
                @enderror
            </div>
        HTML;
    }
}

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.

5 participants