Blade template Laravel

Chào mừng các bạn đã trở lại với học TV, như đã nói ở bài trước thì trong bài hôm nay chúng ta sẽ nói về  "Blade template" trong Laravel. Nội dung bài học này chủ yếu chỉ học cú pháp và lợi ích của từng cú pháp đó, tuy hơi dài nhưng không hề khó. Không luyên thuyên nữa, chúng ta cùng vào bài thôi nào. 

1. Giới thiệu về Blade template Laravel.

Blade là một template engine được cung cấp bởi Laravel. Đây là một engine khá mạnh mẽ, không như những các template engine khác, Blade trong Laravel không hạn chế việc sử dụng mã code PHP thuần túy trong template của bạn ngoài những cú pháp riêng biệt của nó.  Trong thực tế thì Blade template được biên dịch thành các file PHP và được lưu vào cache. Blade template có đuôi mở rộng là .blade.php và được lưu trữ trong thư mục resources/views.

2. Layout template

2.1. Xác định bố cục (Defining layout)

Chúng ta có 2 lợi ích khi sử dụng Blade template đó là layout và section. Để bắt đầu chúng ta sẽ đi vào một ví dụ đơn giản. Như các bạn đã biết, trong một ứng dụng thì đa phần các trang con đều được thừa hưởng một cấu trúc layout chung. Blade template cho phép chúng ta định nghĩa một layout cho ứng dụng với duy nhất một blade view.

resources/views/app.blade.php

<html>
    <head>
        <title>Laravel</title>
    </head>
    <body>
        @section('sidebar')
            <h1>Sidebar</h1>
        @show
        
        @yield('content')
    </body>
</html>

Có thể thấy đây chỉ là nội dung của một đoạn mã HTML bình thường thế nhưng có chút đặt biệt là nó có hai cú pháp lạ: @section, @yield. Đây chính là một trong những cú pháp riêng biệt của Blade template dùng cho mục đích kế thừa qua lại giữa parent-child.

  • @section: dùng để xác định một phần nội dung
  • @yield: dùng để hiển thị nội dung của một phần đã được xác định

Chúng ta sẽ làm sáng tỏ điều này thông qua ví dụ dưới nhé các bạn.

2.2 Kế thừa layout (Extending layout)

Như vừa nói ở trên công dụng chung của @section@yied chính là dùng cho mục đích kế thừa. Blade view app là parent, vì nó là layout cho ứng dụng, vì vậy dưới đây chúng mình sẽ tạo một blade view child đóng vai trò là child kế thừa parent app.

resources/views/child.blade.php

@extends('app')
@section('content')
    <h1>My content</h1>
@endsection

Mình đã sử dụng cú pháp @extends, điều này giúp blade view child có thể kế thừa app. Tiếp theo mình đã xác định nội dung cho section content thông qua thẻ @section. Bây giờ đăng ký route và xem thử nó hoạt động như thế nào.

routes/web.php

Route::get('/', function() {
    return view('child');
});

Ở đây chúng ta render view child chứ không phải app nhé. Kết qua thu được như thế này:

Vì sao section sidebar vẫn được hiển thị? Chúng ta sẽ đi vào ví dụ sau để phân tích lý do nhé.

Cú pháp @section cho section content thì như sau:

resources/views/child.blade.php

@section('content')
    <h1>My content</h1>
@endsection

Nó có thẻ mở @section và thẻ đóng @endsection.

Trong khi đó, lệnh @section cho section sidebar thì khác một chút:

resources/views/app.blade.php

@section('sidebar')
    <h1>Sidebar</h1>
@show

Thẻ đóng lúc này là @show, chính vì thể cú pháp này không chỉ là xác định nội dung cho sidebar mà đồng với với @show nó sẽ render section này luôn. Đây chính là lý do vì sao mà chữ "Sidebar" lại xuất hiện.

Ô thế sao ko dùng @yield luôn cho tiện mà phải dùng @section rồi đóng bằng thẻ @show để render? Lý do là khi sử dụng @section ... @show trong layout thì bạn có thể làm một điều thú vị trong các trang con. 

Ví dụ các bạn mở file resources/views/child.blade.php và thêm đoạn code này vào:

@section('sidebar')
    @parent
    <ul>
        <li>Item 1</li>
        <li>Item 1</li>
    </ul>
@endsection

Giờ cứ thử xem kết quả trước cái đã nào.


Các bạn thấy đấy section sidebar không chỉ không mất chữ "Sidebar" mà còn có thêm cái list mình mới vừa tạo. Bây giờ chúng ta hãy bắt đầu quan sát lại đoạn code vừa thêm ở trên chúng ta có thêm thẻ @parent. Với thẻ này thì các section được định nghĩa lại trong trang con sẽ append vào section ở blade view cha. Trong trường hợp này blade view child đã extend blade view app nên chính vì thế khi khai báo lại section sidebar thì nó đã append thêm cái list ở dưới chữ "Sidebar".

Nếu bỏ @parent đi thì sao nhỉ thế thì section sidebar được khai báo trong blade view child sẽ ghi đè hoàn toàn lên section sidebar ở blade view app.

Nói một chút về @section thì ngoài cách khai báo xác định nội dung trên thì ta còn có một cú pháp khác để xác định nội dung của một phần nào đó như sau:

@section('title', 'Laravel')

Thông thường với cách này thường áp dụng cho các section chứa nội dung ngắn gọn hoặc không chứa các thẻ HTML.

Ngoài ra, @yield còn có thể nhận tham số thứ hai làm giá trị mặc định nếu các trang con extend với nó không xác định nội dung cho phần đã khai báo.

// Nhận giá trị mặc định là view
@yield('content', View::make('view.name'))
// Nhận giá trị mặc định là một chuỗi
@yield('content', 'default')

Lưu ý: Kết thúc mỗi thẻ trong cú pháp Blade template không có dấu ; (chấm phẩy).

3. Component & Slot

Component và slot cung cấp tính năng khá giống với section và layout, nhưng có vài chỗ thì component và slot dễ hiểu hơn nhiều. Hiểu đơn giản thì component là các thành phần, còn slot là nơi chứa nội dung sẽ thay đổi nhiều lần. Hãy tưởng tượng nếu ứng dụng của bạn có nhiều xử lý cần hiện modal để thông báo, nhưng bạn lại muốn có thể sử dụng lại mà không cần phải khai báo nhiều lần. Lúc này component chính là modal, và nội dung thông báo chính là slot.

Thông thường các component được khai báo trong view, bạn có thể tự tổ chức cấu trúc thư mục để lưu trữ các component, hoặc là có thể tham khảo theo cấu trúc của mình.

resources/
├── views/
|   ├── components/
|   |   |   ...

3.1 Khởi tạo component & slot (Creating component & slot)

Bây giờ mình sẽ tạo component modal đặt trong file resources/views/components/modal.blade.php với nội dung sau:

resources/views/components/modal.blade.php

<div class="modal">
    {{ $slot }}
</div>

Cú pháp {{ $slot }} sẽ chứa nội dung thông báo thay đổi liên tục trong suốt ứng dụng.

Lưu ý:: $slot không thể thay đổi tên biến khác.

Mọi thiết lập đã hoàn tất, giờ chỉ cần lấy ra sử dụng thôi. Mình sẽ sử dụng lại resources/views/child.blade.php hồi nãy để test luôn.

resources/views/child.blade.php

@section('content')
    <h1>My content</h1>
    @component('components.modal')
        Đã có lỗi xảy ra
    @endcomponent
@endsection

Chúng ta sử dụng cặp thẻ @component ... @endcomponent để gọi component components.modal, đồng thời khai báo slot. Tham số trong () là tên blade view chứa component, nội dung trong cặp thẻ chính là giá trị mà $slot nhận được. Nạp lại server và chạy đường dẫn http://localhost:8000 ta sẽ được kết quả như sau:

Chúng ta cũng có thể gọi một component nhiều lần như thế này:

@section('content')
    <h1>My content</h1>


    @component('components.modal')
        Đã có lỗi xảy ra
    @endcomponent


    @component('components.modal')
        Đã hết lỗi xảy ra
    @endcomponent
@endsection

3.2 The first available component

Bạn cũng có thể tùy chỉnh các giao diện của component nào đó với @componentFirst, về tính năng thì nó cũng tương tự như View::first.

@componentFirst(['custom.modal', 'modal'])
    Đã có lỗi xảy ra
@endcomponent

3.3 Truyền dữ liệu cho component (Passing data to component)

Đôi khi với một $slot thì bị hạn chế cho việc tùy chỉnh nội dung một component, chính vì thế Laravel cho phép truyền slot khác ngoài $slot mặc định để ứng dụng của bạn linh hoạt hơn.

resources/views/child.blade.php

@component('components.modal')
    @slot('title')
        Lỗi!
    @endslot
    Đã có lỗi xảy ra
@endcomponent

Sử dụng @slot với tham số là tên biến nhận dữ liệu trong blade view component để khai báo một giá trị khác mà component có thể nhận.

Tại component modal, bạn chỉnh sửa nội dung như sau:

resources/views/components/modal.blade.php

<div class="modal">
    <h3>{{ $title }}</h3>
    
    {{ $slot }}
</div>

Laravel sẽ hiểu phần nội dung bạn khai báo ở @slot sẽ truyền vào $title, còn phần chỉ nằm trong cặp thẻ @component thì sẽ truyền vào $slot mặc định. Mình xin tản mạn một chút, cú pháp {{ $varible }} là dùng để hiển thị dữ liệu của biến.

Và đây là kết quả sau khi tải lại trang:

Ngoài ra bạn có thể thay thế cách truyền dữ liệu cho component trên bằng cách:

resources/views/child.blade.php

@component('components.alert', ['title' => 'Lỗi!'])
    Đã có lỗi xảy ra
@endcomponent

Tham số thứ hai trong () sẽ nhận một mảng chứa dữ liệu và lấy tên key làm tên biến trong component. Với cách này thông thường dùng để truyền các dữ liệu ngắn, không chứa thẻ HTML.

3.4 Aliasing component

Bạn có nghĩ chúng ta sẽ có thể tự đặt cú pháp cho component để trông code dễ hiểu, dễ hình dung hơn không, chẳng hạn:

@modal
    Đã có lỗi xảy ra
@endmodal

rất vui là Laravel hiểu mong muốn của bạn, vì thế đã định nghĩa method component trong Blade facade để chúng ta có thể dễ dàng alias cú pháp cho bất kì component nào. Laravel khuyên nên thực hiện việc này tại AppServiceProvider, cụ thể làm method boot:

app/Provider/AppServiceProvider.php

use Illuminate\Support\Facades\Blade;
public function boot() 
{
    Blade::component('components.modal', 'modal');
}

Ở đây bắt buộc chúng ta phải use Blade facade để có thể gọi method component ra. Method này sẽ nhận hai tham số:

  • Tham số thứ nhất là tên component
  • Tham số thứ hai là tên cú pháp thay thế

Bây giờ các bạn có thể khai báo component modal theo cú pháp sau:

// Mặc định
@modal
    Đã có lỗi xảy ra
@endmodal


// Nếu có truyền dữ liệu thêm
@modal(['title' => 'Lỗi!'])
    Đã có lỗi xảy ra
@endmodal

Bạn có thể test để so sánh kết quả, đảm bảo vẫn không thay đổi gì.

Lời kết:

Có lẽ hơi nhức óc rồi phải ko các bạn, tạm thời dừng thư giãn tí nhé, bài học của chúng ta tạm dừng ở đây, chúng ta sẽ tiếp tục tìm hiểu về Blade template Laravel trong bài viết tới nhé!


Bình luận