Học Laravel – Bài 18: Blade

Giới thiệu Blade Templates trong Laravel

Blade template sử dụng .blade.php file extension và lưu trữ ở thư mục resource/views.

Blade có thể return từ route hoặc controller sử dụng method view:

PHP
Route::get('/', function() {
    return view('greeting', ['name' => 'Finn']);
});

Hiển thị dữ liệu trong Blade:

Blade
Hello, {{ $name }}

Current time: {{ time() }}

Nếu không muốn escape data:

Blade
Hello, {!! $name !!}

JSON:

Blade
<script>
    var app = {{ Js::form($array) }}
</script>

Chỉ thị trong Blade:

If else:

Blade
@if (count($records) == 1)
    I have one record.
@elseif (count($record) > 1)
    I have multiple records.
@else
    I don't have any records.
@endif

Unless:

Blade
@unless (Auth::check())
    You are not signed in.
@endunless

Isset, empty:

Blade
@isset($records)
    // $records is defined and is not null ...
@endisset

@empty($records)
    // $records is empty ...
@endempty

Authentication:

Blade
@auth
    // user authenticated
@endauth

@guest
    // user is not authenticated
@endguest

// admin
@auth('admin')
  // admin
@endauth

// not admin
@guest('admin')
  // user not authenticated
@endguest

Môi trường:

Blade
@production
    // abc
@endproduction

@env('staging')

@env(['staging', 'production'])

Section:

Check 1 template thừa kế section có content không:

Blade
@hasSection('navigation')
    <div class="pull-right">
        @yield('navigation')
    </div>
@endif

Switch:

Blade
@switch($i)
    @case(1)
        ...
        @break
    ...
    
    @default
        ...
@endswitch

Lặp:

Blade
@for ($i = 0; $i < 10; $i++)
    //
@endfor

@foreach ($users as $item)

@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>No users</p>
@endforelse

@while (true)
    <p>Ok</p>
@endwhile

Có thể dùng @continue@break trong loop.

Blade
@foreach ($users as $user)
    @if ($user->type == 1)
        @continue
    @endif
    
    @if ($user->number == 5)
        @break
    @endif
@endforeach

// chèn điều kiện trong @continue
@continue($user->type == 1)

@break($user->number == 5)

Sử dụng biến loop:

Biến $loop khả dụng trong vòng lặp foreach:

Blade
@foreach ($users as $user)
    @if ($loop->first)
      This is the first iteration.
    @endif
@endforeach

Vòng lặp lồng có thể gọi parent bằng thuộc tính parent:

Blade
@foreach ($users as $user)
    @foreach ($user->posts as $post)
        @if ($loop->parent->first)
            This is the first iteration of the parent loop.
        @endif
    @endforeach
@endforeach

Các thuộc tính:

Thuộc tínhMô tả
$loop->indexIndex của loop hiện tại (tính từ 0).
$loop->iterationLoop hiện tại (tính từ 1).
$loop->remainingLoop còn lại (tính ngược từ dưới lên).
$loop->countTổng số phần tử trong mảng được loop.
$loop->firstCheck có phải loop đầu tiên.
$loop->lastCheck có phải loop cuối cùng.
$loop->evenCheck loop chẵn.
$loop->oddCheck số lẻ.
$loop->depthSố loop lồng nhau.
$loop->parentGọi biến loop cha ở trong vòng lặp lồng nhau.

Class và Style:

Chỉ thị @class nhận 1 mảng các class thông qua biểu thức điều kiện:

Blade
<span @class(['font-bold' => $isActive, 'gray' => !$isActive, 'red' => $hasError])>{{ $message }}</span>

<span @style(['font-weight: bold' => $isActive, 'color: red' => $hasError])></span>

Thuộc tính mở rộng:

Sử dụng @checked để biểu thị 1 HTML checkbox đã check hay chưa:

Blade
<input type="checkbox" @checked(old('active', $user->active)) />

Tương tự với select input:

Blade
<option value="{{ $version }}" @selected(old('version') == $version)>{{ $version }}</option>

@disbaled để chỉ thị disabled input:

Blade
<button @disable($errors->isEmpty())>Submit</button>

Readonly:

Blade
<input type="text" @readonly($user->isNotAdmin()) />

Required:

Blade
<input type="text" @required($user->isAdmin()) />

Include Sub Views:

Sử dụng @include để include blade view vào view khác:

Blade
<div>
    @include('shared.errors')
</div>

Tất cả biến trong view cha đều có sẵn ở view con.

Bạn cũng có thể pass thêm data cho view con:

Blade
@include('view.name', ['status' => 'complete'])

Include 1 view có thể có hoặc không tồn tại:

// Include 1 view có thể có hoặc không
@includeIf('view.name', ['status' => 'complete'])

Include view với điều kiện:

// Include view với điều kiện:
@includeWhen($boolean, 'view.name')
@includeUnless($boolean, 'view.name')

Include view đầu tiên tồn tại trong 1 mảng cho trước:

// Include view đầu tiên tồn tại trong 1 mảng view:
@includeFirst(['custom.admin', 'admin'])

Render view cho collection:

Blade
@each('view.name', $jobs, 'job')

// truyền tham số thứ 4 trong trường hợp $jobs empty:
@each('view.name', $jobs, 'job', 'view.empty')

View render qua chỉ thị @each không thừa kế biến từ view cha. Nếu muốn view con có những biến này, nên dùng @foreach@include.

Chỉ thị @once:

@once cho phép bạn define 1 phần template mà sẽ chỉ được xử lý 1 lần ở mỗi chu kỳ render. Điều này hữu ích cho việc đẩy những javascript vào header sử dụng stacks. Ví dụ bạn đang render 1 component trong loop, bạn chỉ muốn add javascript vào header ở lần đầu mà component được render:

Blade
@once
    @push('script')
        <script>
            // code
        </script>
    @endpush
@endonce

@once được dùng thường xuyên trong sự liên kết với @push hoặc @prepend, chỉ thị @pushOnce@prependOnce có sẵn cho bạn tiện lợi hơn:

Blade
@pushOnce('scripts')
    <script>
        // code
    </script>
@endPushOnce

Raw PHP:

Blade
@php
    $count = 10;
@endphp

Comment không render ra html:

Blade
{{-- comment will not be present in the rendered HTML --}}

Components trong Blade:

Components và slots cung cấp tính năng tương tự section, layout, include, tuy nhiên bạn có thể tìm thấy component và slot dễ dàng hơn để hiểu. Có 2 cách tiếp cận để viết component: component dựa trên class và component nặc danh.

Tạo component dựa trên class:

ShellScript
php artisan make:component Alert

Câu lệnh trên tạo 1 component trong thư mục app/View/Components. View file sẽ nằm trong resources/view/components. Bạn có thể tạo component trong thư mục con:

ShellScript
php artisan make:component Forms/Input

Nếu bạn muốn tạo component nặc danh, dùng --view:

ShellScript
php artisan make:component forms.input --view

Câu lệnh sẽ tạo blade file tại resources/views/components/forms/input.blade.php, sẽ được render ra 1 component thông qua <x-forms.input />.

Register Package component bằng tay:

Khi viết component cho app, mặc định nó sẽ được tự động đăng ký từ trong thư mục app/View/Componentsresources/views/components. Tuy nhiên nếu bạn build package sử dụng blade, bạn cần register bằng tay component class và HTML tag thay thế của nó. Bạn nên register ở trong method boot ở package service provider:

PHP
use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::component('package-alert', Alert::class);
}

Một khi đã register component, render nó bằng cách dùng alias tag:

Blade
<x-package-alert/>

Bạn có thể dùng componentNamespace method để autoload component class. Ví dụ package Nightshade có thể có CalendarColorPicker component cái mà cư trú ở trong Package\Views\Components namespace:

PHP
use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

Điều này cho phép cách sử dụng component bởi namespace của nó sử dụng cấu trúc package-name::

Blade
<x-nightshade::calendar />
<x-nightshade::color-picker />

Blade sẽ tự động xác định class link đến component này. Thư mục con cũng được hỗ trợ sử dụng “.”

Render component:

Sử dụng tiền tố x-

Nếu component class được lồng sâu hơn trong thư mục app/Views/Components, bạn có thể dùng “.”

Blade
<x-alert/>
<x-user-profile/>

{{-- app/View/Components/Inputs/Button.php --}}
<x-inputs.button/>

Render có điều kiện: định nghĩa method shouldRender. Nếu method return false, component sẽ không được render:

PHP
use Illuminate\Support\Str;

public function shouldRender(): bool
{
    return Str::length($this->message) > 0;
}

Pass data vào component:

Bạn có thể pass data vào blade component sử dụng HTML attributes. Hard-code, giá trị nguyên thủy có thể được pass vào component sử dụng attribute. Biểu thức PHP và biến sẽ được pass vào component thông qua attribute sử dụng dấu : làm tiền tố:

Blade
<x-alert type="error" :message="$message" />

Bạn nên define tất cả component data attribute trong constructor. Tất cả thuộc tính public sẽ được tự động có sẵn trong component view, do vậy không cần thiết pass data trong render:

PHP
class Alert extends Component
{
    public function __construct(public string $type, public string $message)
    {
    
    }
    
    public function render(): View
    {
        return view('components.alert');
    }
}

Ở blade viết như sau:

PHP
<div class="alert alert-{{ $type }}">
    {{ $message }}
</div>

Casing:

Tham số của component constructor sử dụng camelCase, trong khi kebad-case được sử dụng khi tham chiếu tham số trong HTML attribute. Ví dụ:

PHP
public function __construct(public string $alertType)
{}

Trong blade:

PHP
<x-alert alert-type="danger" />

Cú pháp attribute ngắn:

Khi pass attribute vào component, bạn có thể dùng short syntax:

Blade
<x-profile :$userId :$name />

// Tương đương với:
<x-profile :user-id="$userId" :name="$name" />

Escape Attribute Render:

Khi một số JS framework như Alpine.js sử dụng thuộc tính với tiền tố :, bạn có thể sử dụng dấu :: để thông báo với blade rằng thuộc tính không phải là biểu thức PHP. Ví dụ:

Blade
<x-button ::class="{ danger: isDeleting }">Submit</button>

HTML dưới đây sẽ được render bởi Blade:

Blade
<button :class="{ danger: isDeleting }">Submit</button>

Component method:

Để biến public có sẵn trong component, bất kỳ public method trong component đều được gọi. Ví dụ, 1 component có method isSelected:

PHP
public function isSelected(string $option): bool
{
    return $option === $this->selected;
}

Bạn có thể thực thi method này từ component bằng cách gọi biến khớp với tên của method:

Blade
<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">
    {{ $label }}
</option>

Tiếp cận attribute và slot trong component class:

Blade component cũng cho phép bạn tiếp cận tên, thuộc tính, và slot trong method render. Tuy nhiên, để tiếp cận những data này, bạn nên return 1 closure từ method render. Closure sẽ nhận 1 array $data như 1 tham số duy nhất. Array này chứa nhiều element cung cấp thông tin về component:

PHP
use Closure;
 
/**
 * Get the view / contents that represent the component.
 */
public function render(): Closure
{
    return function (array $data) {
        // $data['componentName'];
        // $data['attributes'];
        // $data['slot'];
 
        return '<div>Components content</div>';
    };
}

Thuộc tính componentName tương đương với tên sử dụng trong HTML tag sau tiền tố x-. Ở đây componentName của <x-alert/>alert. Giá trị attributes sẽ chứa tất cả thuộc tính có trong HTML tag. Giá trị slot là 1 instance của Illuminate\Support\HtmlString với content của component slot.

Closure phải trả về 1 string. Nếu string tương ứng với 1 view, view đó sẽ được render, nếu không, string sẽ được đánh giá như 1 inline blade view.

Thêm independencies:

Nếu component yêu cầu independencies từ service container, bạn có thể list chúng trước các thuộc tính của component và nó sẽ tự động được inject:

use App\Services\AlertCreator;
 
/**
 * Create the component instance.
 */
public function __construct(
    public AlertCreator $creator,
    public string $type,
    public string $message,
) {}

Thuộc tính của component:

Thi thoảng bạn cần chỉ định thêm 1 số thuộc tính HTML, ví dụ class. Những thuộc tính này không phải là phần bắt buộc của component. Ví dụ chúng ta cần render 1 alert component như sau:

<x-alert type="error" :message="$message" class="mt-4"/>

Tất cả các thuộc tính mà không có trong constructor của component sẽ được tự động add vào “attribute bag”. Attribute bag này sẽ tự động có sẵn trong component thông qua biến $attributes. Tất cả thuộc tính có thể được render trong component bằng cách echo biến này ra:

<div {{ $attributes }}>
    <!-- Component content -->
</div>

Default / Merged Attributes:

Thi thoảng bạn muốn chỉ định 1 số giá trị mặc định cho thuộc tính, hoặc merge 1 số giá trị vào thuộc tính của component. Sử dụng method merge. Method này hữu ích để thiết lập 1 số class CSS mặc định cho component:

<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
    {{ $message }}
</div>

Như vậy component:

<x-alert type="error" :message="$message" class="mb-4"/>

Sẽ render ra HTML như sau:

<div class="alert alert-error mb-4">
    <!-- Contents of the $message variable -->
</div>

Từ khóa dành riêng:

Một số từ khóa sau được dành riêng cho blade để render component. Những từ khóa dưới đây không được define như 1 thuộc tính public hoặc method trong component: data, render, resolveView, shouldRender, view, withAttributes, withName.

Slots:

Bạn thường xuyên cần pass thêm content vào component thông qua “slots”. Component slots được render bằng cách echo biến $slot. Ví dụ component alert như sau:

<!-- /resources/views/components/alert.blade.php -->
 
<div class="alert alert-danger">
    {{ $slot }}
</div>

Bạn có thể pass content vào slot bằng cách inject content vào trong component:

<x-alert>
    <strong>Whoops!</strong> Something went wrong!
</x-alert>

Thi thoảng 1 component cần render nhiều slots khác nhau ở nhiều vị trí khác nhau trong component. Xem ví dụ sau:

<!-- /resources/views/components/alert.blade.php -->
 
<span class="alert-title">{{ $title }}</span>
 
<div class="alert alert-danger">
    {{ $slot }}
</div>

Bạn có thể khai báo content của slot được đặt tên bằng cách sử dụng tag x-slot:

<x-alert>
    <x-slot:title>
        Server Error
    </x-slot>
 
    <strong>Whoops!</strong> Something went wrong!
</x-alert>

Inline Component Views:

Với component nhỏ, bạn muốn quản lý component class và view template trong 1 file. Sử dụng method render:

/**
 * Get the view / contents that represent the component.
 */
public function render(): string
{
    return <<<'blade'
        <div class="alert alert-danger">
            {{ $slot }}
        </div>
    blade;
}

Dynamic Components:

Thi thoảng bạn muốn render 1 component nhưng không biết component nào sẽ được render cho đến khi runtime. Tình huống này, bạn có thể sử dụng dynamic-component để render dựa trên 1 giá trị hoặc biến tại thời điểm runtime:

// $componentName = "secondary-button";
 
<x-dynamic-component :component="$componentName" class="mt-4" />

Anonymous Components:

Bạn chỉ cần tạo file blade trong thư mục resources/views/components. Ví dụ file alert.blade.php, bạn chỉ cần gọi tương ứng như sau:

<x-alert/>

Bạn có thể dùng . để biểu thị 1 component trong 1 thư mục con. Ví dụ file resources/views/components/inputs/button.blade.php có thể được render như sau:

<x-inputs.button/>

Anonymous Index Components:

Thi thoảng 1 component được tạo thành bởi nhiều blade template. Bạn muốn group các template đó lại trong 1 thư mục. Ví dụ component “accordion”:

/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php

Sau đó bạn có thể render accordion component và các item của nó như sau:

<x-accordion>
    <x-accordion.item>
        ...
    </x-accordion.item>
</x-accordion>

Tuy nhiên, để render accordion component thông qua x-accordion, chúng ta cần phải đặt “index” accordion component template trong thư mục resources/views/components thay vì để lồng trong thư mục accordion với các template khác.

May thay, Blade cho phép bạn đặt file index.blade.php trong thư mục của component. Khi template này tồn tại, nó sẽ được render thành “root” node của component. Vì vậy chúng ta có thể tiếp tục dùng cùng cú pháp blade đã cho ở ví dụ trên, tuy nhiên sẽ sửa lại cấu trúc thư mục như sau:

/resources/views/components/accordion/index.blade.php
/resources/views/components/accordion/item.blade.php

Data Properties / Attributes:

Vì anonymouse components không có class tương ứng, bạn có thể thắc mắc làm sao để phân biệt data nào là biến, data nào là “attribute bag”.

Bạn có thể chỉ định thuộc tính nào được xem như là biến bằng cách sử dụng chỉ thị @props ở đầu component. Tất cả những thuộc tính khác sẽ là “attribute bag”. Nếu bạn muốn cung cấp 1 giá trị mặc định cho 1 biến, bạn có thể chỉ định tên biến như 1 array key và giá trị mặc định là array value:

<!-- /resources/views/components/alert.blade.php -->
 
@props(['type' => 'info', 'message'])
 
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
    {{ $message }}
</div>

Component ở trên có thể render như sau:

<x-alert type="error" :message="$message" class="mb-4"/>

// render ra HTML như sau:
<div class="alert alert-error mb-4">
  // message
</div>

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *