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

[7.x] Implement anonymous components #31363

Merged
merged 30 commits into from
Feb 14, 2020
Merged

Conversation

driesvints
Copy link
Member

@driesvints driesvints commented Feb 5, 2020

This PR introduces class-less components à la Blade X. What it does under the hood is make use of a ClassLessComponent class (wait whut?) to pipe through all the attributes as data to the component. This way you can do this:

welcome.blade.php

<x-avatar size="100" />

components/avatar.blade.php

@php($user = $user ?? Auth::user())
@php($size = $size ?? 50)

<img
    class="inline-block rounded-full"
    src="{{ $user->gravatarUrl($size) }}"
    width="{{ $size }}"
    height="{{ $size }}"
/>

All without a component class. This will be super useful for very simple small components and composition where you don't always need a class for.

Update

Decided to rename ClassLessCompnent to AnonymousComponent.

@driesvints driesvints force-pushed the class-less-components branch from ea9e861 to 5ffac6d Compare February 5, 2020 15:23
@driesvints driesvints changed the title [7.x] Implement class-less components [7.x] Implement anonymous components Feb 5, 2020
@taylorotwell
Copy link
Member

With class based components you can use colons to indicate directories like:

<x-inputs:text />

Will resolve to App\View\Components\Inputs\Text.php by default. Do these anonymous components allow you to do that so <x-inputs:text /> as an anonymous component would resolve to views/components/inputs/text.blade.php?

@ryangjchandler
Copy link
Contributor

ryangjchandler commented Feb 6, 2020

With class based components you can use colons to indicate directories like:

<x-inputs:text />

Will resolve to App\View\Components\Inputs\Text.php by default. Do these anonymous components allow you to do that so <x-inputs:text /> as an anonymous component would resolve to views/components/inputs/text.blade.php?

At a quick glance, I'm not sure it will since the AnonymousComponent class is just receiving the FQN of what would be the defined component's class. The view() method on the AnonymousComponent class would need to handle the magic, transform the class name to a valid Blade path, something like below.

function view()
{
    return collect(explode('\\', Str::after($this->view, '\\View\\Components\\')))->map(function ($segment) {
        return Str::kebab($segment);
    })->implode('.');
}

If $this->view has been set to App\\View\\Components\\Test\\Component, the output would be test.component.

This would also work for tags such as <x-base:alertComponent or <x-base:alert-component>, assuming the class generated by guessClassName is App\\View\\Components\\Base\\AlertComponent, it would return a view of base.alert-component. The problem I can see here though is not everyone will use kebab case for their view names, so there would probably need to be some checks for other cases too.

@laravel laravel deleted a comment from garygreen Feb 6, 2020
@laravel laravel deleted a comment from ryangjchandler Feb 6, 2020
@laravel laravel deleted a comment from ryangjchandler Feb 6, 2020
@laravel laravel deleted a comment from garygreen Feb 6, 2020
@taylorotwell
Copy link
Member

taylorotwell commented Feb 14, 2020

I'm also curious how something like <x-anon-component wire:model="foo"> would work? Since wire:model is not a valid PHP variable name? It seems like you would actually have to put everything in an attribute bag instead of making them variables?

@taylorotwell
Copy link
Member

@calebporzio suggested perhaps a @props directive at the top of anon components that specifies which variables should be extracted from the attribute bag and made real variables...

image

@driesvints driesvints force-pushed the class-less-components branch from b3ead30 to f8d0cf7 Compare February 14, 2020 17:09
@driesvints driesvints force-pushed the class-less-components branch from f8d0cf7 to 6275dd4 Compare February 14, 2020 17:32
@garygreen
Copy link
Contributor

garygreen commented Feb 14, 2020

I'm also curious how something like would work? Since wire:model is not a valid PHP variable name

This is why this PR and Blade X being appealing is so strange to me. That is completely valid in html/js and you could access that attribute via el.getAttribute('wire:model') in PHP it doesn't make sense and your wrapping your component logic into a HTML tag and then destructing it back into PHP - why?

In Vue this is the main way of including components and exposing props - it solves a complicated problem on the frontend, but in PHP that simply isn't a problem and doesn't solve anything. It just creates an unnescary intermediary. Rendering on the server is totally different to rendering client side.

Anyway, that's just my take on it. I'm sure this comment will get deleted. 😋

@taylorotwell
Copy link
Member

taylorotwell commented Feb 14, 2020

Actually I think it is solving a lot of problems - just because you don't see them doesn't mean they don't exist... 😄 Comments get deleted if they are off topic.

Here is a problem it solves for example. Say I'm building a package that ships a nice "map" component that uses Blade component tags. It accepts some data as props but I also want to allow developers to be able to add a class to it to supply some margin or something (or any other arbitrary HTML attribute) but I also need some default values on the class:

<x-map :target="map" class="mb-4 border-2" />

Within the map component:

<div {{ $attributes->merge(['class' => 'flex items-center']) }}>
   Do something with {{ $target }}
</div>

The <div> gets output as <div class="flex items-center mb-4 border-2">.

Managing the $attributes part of this is very cumbersome to do with the current state of Blade components. The new component tags make it a cinch and opens up the door to build more robust components with MUCH more robust customization that is easy to manage in the mark-up.

@driesvints driesvints force-pushed the class-less-components branch from 70b8399 to d9c0788 Compare February 14, 2020 21:40
@driesvints
Copy link
Member Author

Is this capable of handling a body of text?

Yeah slots just work as normal.

How do you supply dynamic options to it? Looking at the docs of BladeX it looks like it would be:

You can do that here as well.

Also does the blade compiler put the "compiled" version of the code in the view or does it have to create the anonymous class object every time for every component included on the page? If you had thousands of inputs it may cause slower rendering compared to a custom optimized blade directive:

This isn't a problem because views are compiled and cached.

@taylorotwell
Copy link
Member

taylorotwell commented Feb 14, 2020

Yes, it handles slots just like normal components.

Yes, it handles dynamic options exactly like Blade X (same syntax there).

Yes, it creates a class to use a component. People can weigh the performance benefits if they wish and use them or not use them. If you're rendering thousands of input components with lots of custom attributes then it's probably going to be slow. People doing that can take a different approach.

@taylorotwell
Copy link
Member

taylorotwell commented Feb 14, 2020

@garygreen For reference... rendering 1000 components on a screen takes about 45ms on my machine. So, yes it can be "slow". Rendering 1000 @include statements takes about 30ms.

A more "typical" page rendering 100 components takes about 5-6ms to render (vs about 4ms using @include). So about a 1-2ms penalty there.

@taylorotwell taylorotwell merged commit b1fe9eb into master Feb 14, 2020
@driesvints driesvints deleted the class-less-components branch February 14, 2020 23:09
voku added a commit to voku/framework that referenced this pull request Feb 15, 2020
* upstream/master: (78 commits)
  [7.x] Implement anonymous components (laravel#31363)
  Apply fixes from StyleCI (laravel#31480)
  Apply fixes from StyleCI (laravel#31479)
  revert broken table feature
  move files
  add test for event payload of type object (laravel#31477)
  [7.x] Refactor route caching (laravel#31188)
  [6.x] Fixes appendRow on console table (laravel#31469)
  Apply fixes from StyleCI (laravel#31474)
  formatting
  Throw exception on empty collection (laravel#31471)
  Add tests for Query Builder when array value given (laravel#31464)
  Remove addHidden method (laravel#31463)
  allow afterResponse chain
  add getRawOriginal
  Remove unused use-statement
  Update comment
  [6.x] Change MySql nullable modifier to allow generated columns to be not null (laravel#31452)
  [6.x] Test for pushed events (laravel#31451)
  Fixed phpdoc
  ...
@mojtabaahn
Copy link

$attributes return nothing currently when using anonymous components..

@mojtabaahn
Copy link

Aright nvm, It used to not working on 7.x-dev but in 7.0.2 is working fine!

@devfaysal
Copy link

devfaysal commented Mar 28, 2020

If I have a package that has some anonymous components, the only way I can make them available is to create a class for them and register them individually. Is there some (undocumented) way of loading anonymous components from the package?
Any idea @driesvints @taylorotwell

@driesvints
Copy link
Member Author

@devfaysal
Copy link

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

Thanks for quick reply. I created an issue #32154 about it.

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.

8 participants