laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.12k stars 10.87k forks source link

Blade attributes not being passed through to class #31919

Closed booni3 closed 4 years ago

booni3 commented 4 years ago

Description:

This is an issue only on Vapor currently for me. On my local valet install with PHP 7.4.3 it works fine.

Additional attributes passed into the x-blade tag are not being correctly passed into the class.

Failure Mode 1 "Undefined variable" exception is thrown.

class button extends Component
{
    public $colour;

    public function __construct($colour)
    {
        $this->colour = $colour;
    }
...
<x-button class="def" colour="red">Save</x-button>
<button type="button" {{ $attributes->merge(['class' => "abc  ".$colour]) }}>
  {{ $slot }}
</button>

Failure Mode 2

class button extends Component
{
    public $c;

    public function __construct($colour = null)
    {
        $this->c = $colour ?? 'blue';
    }
...
<x-button class="def" colour="red">Save</x-button>
<button type="button" {{ $attributes->merge(['class' => "abc  ".($c ?? 'undefined')]) }}>
  {{ $slot }}
</button>

This provides the html output of:

<button type="button" class="abc undefined def" colour="red">
    Save
</button>

but we were expecting

<button type="button" class="abc def red">
    Save
</button>

Steps To Reproduce:

driesvints commented 4 years ago

Think this is fixed with https://github.com/laravel/framework/pull/31932 ? Can you update to v7.1.3 and retry?

booni3 commented 4 years ago

This seems to be partially fixed but I still get errors when trying to modify/reassign the variable. This may not be how the components are supposed to work (but I think they are). Again it all works locally, but not in production on Vapor.

This works Passing the class through directly without modification

<!-- button.blade.php -->
<button type="button" {{ $attributes->merge(['class' => "inline-flex items-center ".$colour]) }}>
  {{ $slot }}
</button>
// class
class button extends Component
{
    public $colour;

    public function __construct($colour = null)
    {
        $this->colour = $colour;
    }
...
<!-- usage -->
<x-button class="-ml-px relative" colour="bg-gray-100">
  Status
</x-button>
<!-- result -->
<button type="button" class="inline-flex items-center bg-gray-100">
   Status
</button>

Does not work 1 When the variable is altered in the constructor it gets pulled through as a separate attribute and not merged to the class list.

<!-- button.blade.php -->
<button type="button" {{ $attributes->merge(['class' => "inline-flex items-center ".$colour]) }}>
  {{ $slot }}
</button>
// class
class button extends Component
{
    public $colour;

    public function __construct($colour = null)
    {
        $this->colour = ($colour == "gray" ? "bg-gray-100" : "")
    }
...
<!-- usage -->
<x-button class="-ml-px relative" colour="gray">
  Status
</x-button>
<!-- result -->
<button type="button" class="inline-flex items-center" colour="gray">
   Status
</button>

Does not work 2 Assigning a new public variable results in an exception.

<!-- button.blade.php -->
<button type="button" {{ $attributes->merge(['class' => "inline-flex items-center ".$c]) }}>
  {{ $slot }}
</button>
// class
class button extends Component
{
    public $c;

    public function __construct($colour = null)
    {
        $this->c = $colour
    }
...
<!-- usage -->
<x-button class="-ml-px relative" colour="gray">
  Status
</x-button>

Throws exception "Undefined variable: c"

driesvints commented 4 years ago

If this doesn't work for you on vapor please open a ticket through vapor support or try a support channel. Thanks.

themsaid commented 4 years ago

Re-opening this to see if anyone else can replicate or help debug.

darbelaez commented 4 years ago

I seem to have a similar issue on Laravel 7.1.3 - while it works in my development environment (MAMP Pro PHP 7.2.20 on MacOS) it does not work on a shared hosting environment (HostGator PHP v7.2.26) - i'm getting a "variable not defined" - seems the constructor for the component is not called perhaps?

<!-- App/Components/Layout/ContentHeader.php -->
<?php

namespace App\View\Components\layout;

use Illuminate\Support\Facades\Request;
use Illuminate\View\Component;

class ContentHeader extends Component
{
    /**
     * @var string
     */
    public $page_title;

    /**
     * @var array
     */
    public $breadcrumbs = [];

    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    $this->page_title = 'Some title';
    $crumb = new \stdClass();
        $crumb->label = 'Home';
        $crumb->active = false;
        $this->breadcrumbs[] = $crumb;

        $crumb = new \stdClass();
        $crumb->label = $this->page_title;
        $crumb->active = true;
        $this->breadcrumbs[] = $crumb;
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('components.layout.content-header');
    }
}
<!-- Resources/views/components/layout/content-header.blade.php -->
<div class="content-header">
    <div class="container-fluid">
        <div class="row mb-2">
            <div class="col-sm-6">
                <h1 class="m-0 text-dark">{{ $page_title }}</h1>
            </div><!-- /.col -->
            <div class="col-sm-6">
                <ol class="breadcrumb float-sm-right">
                    @foreach ($breadcrumbs as $crumb)
                        @if (!$crumb->active)
                            <li class="breadcrumb-item"><a href="#">{{ $crumb->label }}</a></li>
                        @else
                            <li class="breadcrumb-item active">{{ $crumb->label }}</li>
                        @endif
                    @endforeach
                </ol>
            </div><!-- /.col -->
        </div><!-- /.row -->
    </div><!-- /.container-fluid -->
</div>
<!-- /.content-header -->
briavers commented 4 years ago

I have the same/simmilar issue

My resources/views/pages/book-estimate.blade.php


@section('content')
    <div class="row">
        <div class="col-sm-12 col-md-6">
            <h3 class="text-center mb-3"> Schade? Maak online een afspraak voor een bestek! </h3>

            <x-form-elements.location-picker type="error"/>

        </div>
....
....
...
@endsection

My resources/views/components/form-elements/location-picker.blade.php

<div class="form-group">
    <label>{{ __('Location') }}</label>
    <select class="form-control"  [formControl]="location">
        {{ $classForType }}
        <option>1</option>
        <option>2</option>
    </select>
</div>

My app/View/Components/formElements/locationPicker.php

<?php

namespace App\View\Components\formElements;

use Illuminate\Support\Facades\Http;
use Illuminate\View\Component;
use Illuminate\View\View;

class locationPicker extends Component
{
    /**
     * The alert type.
     *
     * @var string
     */
    public $type;

    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct($type)
    {
        $this->type = $type;

    }

    /**
     * Get the class for the given alert type.
     *
     * @return string
     */
    public function classForType()
    {
        return $this->type == 'danger' ? 'alert-danger' : 'alert-warning';
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return View|string
     */
    public function render()
    {
        return view('components.form-elements.location-picker');
    }

}

The error

Facade\Ignition\Exceptions\ViewException Undefined variable: classForType (View: /code/autotechnica/resources/views/components/form-elements/location-picker.blade.php)

The docs

https://laravel.com/docs/7.x/releases#laravel-7

I also tried with the blade part of the docs but same issue

briavers commented 4 years ago

I just fixed my issue by regestering my component in the Boot function of the AppServiceProvidor under a different name Blade::component('location-picker', locationPicker::class);

booni3 commented 4 years ago

I can confirm the above from @briavers. If you manually register the component, then it all works as expected. It looks like there may, therefore, be an issue with auto-discovery?

booni3 commented 4 years ago

Additionally, if you name your class Button.php, as opposed to button.php this works too. If you check the docs, all the examples are actually capitalized too.

https://laravel.com/docs/7.x/blade#components

@driesvints, not sure if this is intended behaviour or something that should be captured?

njoguamos commented 4 years ago

I was am experiencing the same issue. However in my case, the components were working as intended on local environment and failing on production.

I renamed the components as suggested by @booni3

Screenshot 2020-04-08 at 17 52 37

and now everything works on my production environment.

driesvints commented 4 years ago

Classes are in general PascalCase and you should always name them as such.

yob-yob commented 4 years ago

So Just going to add a quick bit of information...

I've tried Changing my Class names as suggested by @booni3 yet, which I had no luck.

so I run my old envoy deploy script and boom it works.. I checked and it's composer dump-autoload that fixed the issue for me.

I tested it by updating on my Component class then manualy pushing the to staging, my update didn't worked, after I run composer dump-autoload it worked. Magic. 🧙‍♂️

sblawrie commented 3 years ago

I ran into this issue, too. Works in dev, fails in production.

Uppercasing the classname of the component did not fix it. Running composer dump-autoload --optimize did not fix it. Even adding Blade::component('component-name', ComponentName::class); into the AppServiceProvider class did not fix it.

The only thing that fixed it was adding Blade::component('component-name', ComponentName::class); to the AppServiceProvider and changing the name of the component to something like Blade::component('component-name-new', ComponentName::class);

Would be great if this bug got fixed so we can use Blade components reliably moving forward!

adampatterson commented 3 years ago

I had all kinds of issues, ultimately I Created a fresh component, and everything worked.

It did work fine locally on macOS it was only on my server did I have troubles.

What was really strange is that when I changed the component render path to be wrong it did trigger an error, so the class was working fine. 🤷‍♂️

ahinkle commented 3 years ago

@driesvints

I've also run into this problem a few times. Would it be worthy sending in a PR to automatically PascalCase generated components?

Current artisan make:component foo

class foo extends Component

Suggested (with PR): artisan make:component foo

class Foo extends Component
driesvints commented 3 years ago

Maybe yeah. Feel free to try one to see if Taylor will accept it 👍

adampatterson commented 3 years ago

@ahinkle It might be better to warn or not allow the wrong format rather than interpreting it 🤷‍♂️

driesvints commented 3 years ago

A warning might be better indeed.

ehsantalkhouncheh commented 3 years ago

I have an issue about that in laravel 8 when i try use props i always got and error about undifiend variable $variablename i got this error just on mac os i tried to change component class name and also i tried to register component manually in appServiceprovider boot method but i had no luck `

namespace App\View\Components;

use Illuminate\View\Component;

class Alert extends Component { /**

blade: @props(['frontContent']) div {{$frontContent}} /div

driesvints commented 3 years ago

@ehsantalkhouncheh please try a support channel.

basharsgnv commented 3 years ago

I had the same issue and finally I discovered the issue after half day of work. check the component folder should start with uppercase!

aabadawy commented 3 years ago

@basharsgnv Thanks Man, Finally, it works fine.

marksparrish commented 3 years ago

OK - having the same misunderstanding of how to display and use the component when it is in a subdirectory of the components views (e.g. table.row).

https://laravel.com/docs/8.x/blade#rendering-components

I was moving the blade template to a subdirectory in the components views and calling the view like <x-subdirectory.component_name> You will call the component blade template directly. Bypassing the component class. The documentation is good but not quite clear enough.

php artisan make:component Alert
<x-alert/>

php artisan make:component Forms/Input
<x-forms.input/>
itas-bill-dou commented 2 years ago

The following commands fixed my issue (I used "Anonymous Components"):

php artisan optimize:clear
composer dump-autoload
unohuim commented 2 years ago

The following commands fixed my issue (I used "Anonymous Components"):

php artisan optimize:clear
composer dump-autoload

this specifically solved my issue. Thanks

KotKHPI commented 10 months ago

No one methods is helped me, but I found solve:

In app/Component/View/filename.php you must add

class NameOfComponent extends Component
{
    /**
     * Create a new component instance.
     */
    public function __construct(public readonly ?**type_of_your_variable** $**your_variable**)
    {
        //
    }

    /**
     * Get the view / contents that represent the component.
     */
    public function render(): View|Closure|string
    {
        return view('components.card-author');
    }
}