<?phpnamespaceApp\View\Components;useIlluminate\View\Component;classLayoutextendsComponent{/** * Create a new component instance. * * @returnvoid */publicfunction__construct() {// }/** * Get the view / contents that represent the component. * * @return\Illuminate\Contracts\View\View|\Closure|string */publicfunctionrender() {returnview('components.layout'); }}
<?phpnamespaceApp\View\Components;useIlluminate\View\Component;classLayoutextendsComponent{/** * Create a new component instance. * * @returnvoid */publicfunction__construct() {// }/** * Get the view / contents that represent the component. * * @return\Illuminate\Contracts\View\View|\Closure|string */publicfunctionrender() {returnview('components.layout'); }}
<?phpnamespaceApp\View\Components;useIlluminate\View\Component;classLayoutextendsComponent{/** * Create a new component instance. * * @returnvoid */publicfunction__construct() {// }/** * Get the view / contents that represent the component. * * @return\Illuminate\Contracts\View\View|\Closure|string */publicfunctionrender() {returnview('components.layout'); }}
5. Quick Introduction to Anonymous Blade Components In Laravel 7
5.1. Giao diện thường
C:\xampp8\htdocs\testauth\routes\web.php
<?phpuseIlluminate\Support\Facades\Route;/*|--------------------------------------------------------------------------| Web Routes|--------------------------------------------------------------------------|| Here is where you can register web routes for your application. These| routes are loaded by the RouteServiceProvider within a group which| contains the "web" middleware group. Now create something great!|*/Route::get('/',function () {returnview('profile');});Route::get('profile',function () {returnview('profile');});
<?phpuseIlluminate\Support\Facades\Route;/*|--------------------------------------------------------------------------| Web Routes|--------------------------------------------------------------------------|| Here is where you can register web routes for your application. These| routes are loaded by the RouteServiceProvider within a group which| contains the "web" middleware group. Now create something great!|*/Route::get('/',function () {returnview('profile');});Route::get('profile',function () {returnview('profile');});
<?phpuseIlluminate\Support\Facades\Route;/*|--------------------------------------------------------------------------| Web Routes|--------------------------------------------------------------------------|| Here is where you can register web routes for your application. These| routes are loaded by the RouteServiceProvider within a group which| contains the "web" middleware group. Now create something great!|*/Route::get('/',function () { $data = ['first_name'=>'Shane','last_name'=>'Rosenthal','email'=>'srosenthal82@gmail.com', ];returnview('profile', compact('data'));});Route::get('profile',function () {returnview('profile');});
In the latest blog post I showed you how Laravels Blade view components work and how you can make use of them. In this part of the series, I want to focus on how we can use these components throughout our application - starting with the very basic: your layout.
In a typical Laravel application, you might have a file called layouts/app.blade.php that all of your other views extend from.
To make this work, the only thing we need to do in our welcome.blade.php file is, instead of extending our layout - we can just wrap all of the content inside of our new layout component, like this:
<x-layout>
@section('scripts')
<!-- Some JS and styles -->
@endsection
@section('content')
<div>My Page content is here</div>
@endsection
</x-layout>
This is already pretty good, but as we have seen in the previous post, Laravel provides a $slot variable that automatically holds all of the content that was placed inside of the blade component tags. So lets go and change our layout to make use of the $slot variable, instead of yielding a content section:
Instead of using the @section directive, we can now simply pass the data as is:
<x-layout>
@section('scripts')
<!-- Some JS and styles -->
@endsection
<div>My Page content is here</div>
</x-layout>
Alright - so we got rid of the content section and wrapped everything into our layout component. That's already pretty cool and I think this improves the readability of our view component. Because of the indentation it is immediately clear which layout is being used. This scripts section is still bothering me though. So let's see how we can get rid of this as well - and there are multiple options.
Named Slots
The first option that we have is to make use of a "named slot". We already discussed the $slot variable, that will automatically be populated with everything within the component HTML, but we can also manually specify a slot name. This can be done using the x-slot tag - which itself is sort of a pre-defined Blade component that comes out of the box with Laravel.
By passing a name property to our x-slot, we can make the data available within a variable with the same name as our name attribute.
Let's modify our layout component to make use of a new $scripts variable:
To pass this variable to our view, we can now pass it into a scripts slot:
<x-layout>
<x-slot name="scripts">
<!-- Some JS and styles -->
</x-slot>
<div>My Page content is here</div>
</x-layout>
Refresh the page, and you can see that everything works as expected. With this, we introduced an error though, as our $scripts variable is now mandatory within our layout - so if a view would not provide a scripts slot, you would get an error "Undefined variable: scripts".
While this works with a named slot, we no longer have the ability to append to the scripts section from within multiple of our views/components - because we don't have a real "section" anymore.
So what if we want to get rid of the @section directives and use a component for this instead? First of, lets get our @yield directive back into our layout component:
Alright - now lets see how we can make use of this section, without using a directive.
Renderless Blade components
When you create a custom Blade component, the class has a render method, that by default returns a string containing the name of the view for the given component:
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{
public function render()
{
return view('components.alert');
}
}
There's another way of returning a view though, which is by returning a callable, that is then going to return the actual view. In this case, our component only holds the data/state that was provided for the component, but it doesn't actually return a view - which is great for our section use case.
So lets create a new view component called Section:
php artisan make:component Section
Now instead of returning a view, we are actually going to return a function - that will return the @section directive for us:
namespace App\View\Components;
use Illuminate\View\Component;
class Section extends Component
{
public function render()
{
return function ($componentData) {
return <<<BLADE
@section("{$componentData['attributes']->get('name')}")
{$componentData['slot']}
@endsection
BLADE;
};
}
}
Alright - so lets break this render method down line by line.
The render method returns a callable with one parameter - an array holding the component data. This array has the following data and keys available:
componentName - The name for the component, in our case "section"
attributes - The Illuminate\View\ComponentAttributeBag instance that holds all of our component attributes
slot - A Illuminate\Support\HtmlString object with the string that was passed to the default slot
Inside of this callable, we are returning a string - this string is escaped using PHPs Heredoc Syntax.
Inside of this string, we can now return any Blade code that we want - so in our case, we are opening a @section directive, with the name attribute that was provided in the <x-section tag. Inside of that @section directive, we pass the slot of our component (and cast it to a string, as it's a HtmlString object). And last but not least, we close the section directive.
To make use of this renderless component, we can now rewrite our page view like this:
<x-layout>
<x-section name="scripts">
<!-- Some JS and styles -->
</x-section>
<div>My Page content is here</div>
</x-layout>
Yay - we did it! We got rid of our ugly directive and have successfully refactored it to make use of a custom blade component.
Is this better than using the directives? You'll have to decide for yourself - but I hope that this inspired you to take a look at renderless Blade components, as well as how you can use components for your traditional layout extensions.