Powerful, dynamic, front-end UIs without leaving PHP.
simple laravel project create
- write the command livwire install
composer require livewire/livewire
- remove
welcome.blade.phpjust using this welcome.blade.php
<Livewire:hello-word/>-
then write the command
php artisan make:livewore hello-word
-
then open the
hello-word.blade.php
this is a current time: {{ time() }}
<button wire:click="$refresh">Refresh</button>Responding to button presses and other events in Livewire is a breeze. we'll add a button to the page and wire up functionality to it using "actions" in Livewire.
- php artisan make:livewire Counter
- open controller file
Counter.php
class Counter extends Component
{
public $count = 1;
public function render()
{
return view('livewire.counter');
}
public function increment()
{
$this->count++;
}
public function decrement($by)
{
$this->count = $this->count - $by;
}
}- open view file
counter.blade.php
<div>
Count: {{ $count }}
<button wire:click="increment">+</button>
<button wire:click="decrement(2)">-</button>
</div>Properties in Livewire allow you to store and track state in your component. This is a fundamental concept to understand when using Livewire and allows you to build forms and other interfaces with ease.
- php artisan make:livewire Todos
- open
Todos.php
<?php
namespace App\Livewire;
use Livewire\Component;
class Todos extends Component
{
public $todo ='';
public $todos = [
'Learn Livewire',
'Build a Livewire app',
'Profit!',
];
public function add(){
$this->todos[] = $this->todo;
$this->reset('todo');
}
public function render()
{
return view('livewire.todos');
}
}- open
todos.blade.php
<div>
<input type="text" wire:model='todo' placeholder="Add a todo...">
<button wire:click='add'>Add Todo</button>
<ul>
@foreach($todos as $todo)
<li class="border-b py-2">{{ $todo }}</li>
@endforeach
</ul>
</div>- open
todos.blade.php
<form wire:submit='add'>
<h2>My Todo List</h2>
<input type="text" wire:model.live.debounce.5ms='todo' placeholder="Add a new todo..."/>
<button type='submit'>Add Todo</button>
</form>
<span>Current todo: {{ $todo }}</span>
<hr/>
<ul>
@foreach($todos as $todo)
<li class="border-b py-2">{{ $todo }}</li>
@endforeach
</ul>Let me introduce you to a few important lifecycle hooks in Livewire. We'll cover "mount" and "updated" in this video. Both of these are useful for doing any custom initialization on a page as well as running custom code after every property update.
public function mount(){
$this->todos =[
'Learn Livewire',
'Build a Livewire app',
'Profit!',
];
}
public function updated($property,$value){
// dd($property,$value);
$this->$property = strtoupper($value);
}So far we've built basic, standalone, components. I'll introduce you to the concept of "Page Components". These allow you to use a Livewire component as an entire page in your app. We'll cover things like routing, page layouts, and page titles.
- php artisan livewire:layout
- open the
app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ $title ?? 'Page Title' }}</title>
<style>
.current{font-weight: 700;}
</style>
</head>
<body>
<nav>
<a href="/" @class(['current'=>request()->is('/')])>Todos</a>
<a href="/counter" @class(['current'=>request()->is('counter')])>Clicker</a>
</nav>
{{ $slot }}
</body>
</html>- set the routes in
web.php
Route::get("/",Todos::class);
Route::get("/counter",Clicker::class);Before we go any further, let's build a basic table with data from an Eloquent model. Livewire makes it easy to display data as rows in a table and add things like "delete" buttons to each row.
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach ($this->users as $user)
<tr wire:key="{{ $user->id }}">
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td><button type="button" wire:click="delete({{ $user->id }})" wire:confirm="Are you sure want to delete this??">Delete</button></td>
</tr>
@endforeach
</tbody>
</table>public function delete(User $user){
$user->delete();
}If tables are one essential component in every app, forms are the other. we'll walk through building a basic form in your application. We'll cover all the essentials from validation, to submission, to redirecting after.
// #[Rule('required',as: 'Title')]
#[Rule('required',message: 'Yo, add a Name')]
#[Rule('min:4',message: 'Yo, too short')]
public $name = '';
#[Rule('required|email|max:30|unique:users')]
public $email;
#[Rule('required|min:5|max:16')]
public $password;
public function save(){
$this->validate();
User::create([
'name'=>$this->name,
'email'=>$this->email,
'password'=>$this->password
]);
$this->redirect('/');
// dd("saved!");
}<form wire:submit="save">
<label>
<span>Name</span>
<input type="text" placeholder="Name" id="name" wire:model="name" />
@error('name')
<em>{{ $message }}</em>
@enderror
</label>
<label>
<span>E-mail</span>
<input type="email" placeholder="E-Mail" id="" wire:model="email" />
@error('email')
<em>{{ $message }}</em>
@enderror
</label>
<input type="password" placeholder="Password" id="password" wire:model="password" />
@error('password')
<em>{{ $message }}</em>
@enderror
<button type="submit"><span>Submit</span></button>
</form>AlpineJS is the front-end companion framework that comes bundled with every Livewire application. In fact, Livewire's front-end is built on top of Alpine itself. We'll walk through a few common use cases and how you can use Alpine to add interactivity into your Livewire components without making server roundtrips.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ $title ?? 'Page Title' }}</title>
@livewireStyles
</head>
<body>
{{ $slot }}
@livewireScripts
</body>
</html><div x-data="{count:0}">
<span x-text="count"></span>
<button x-on:click="count++">+</button>
</div>
<h3>Current Name: <em x-text="$wire.name.length"></em></h3>
<button x-on:click="$wire.name=''">Clear Name</button>
<button x-on:click="$wire.save()">Submit Form</button>
{{-- <form wire:submit="save"> --}}
<form x-on:submit="$wire.save()">
<label>
<span>Name</span>
<input type="text" placeholder="Name" id="name" wire:model="name" />
<small>Words:<span x-text="$wire.name.split(' ').length - 1"></span></small>
@error('name')
<em>{{ $message }}</em>
@enderror
</label>
<label>
<span>E-mail</span>
<input type="email" placeholder="E-Mail" id="" wire:model="email" />
<small>Characters:<span x-text="$wire.email.length">0</span></small>
@error('email')
<em>{{ $message }}</em>
@enderror
</label>
<input type="password" placeholder="Password" id="password" wire:model="password" />
@error('password')
<em>{{ $message }}</em>
@enderror
<button type="submit"><span>Submit</span></button>
</form>Livewire provides lots of helpful tools for building automated tests for your components. we'll build a few basic tests for the form component we built earlier.
php artisan make:livewire create-post --test- vscode extension
Better PHPUnit
Livewire allows you to nest components within each other. This is an extremely powerful technique, however, there are important cavaets and knowledge to be aware of when nesting.
php artisan make:livewire user-row- open the
user.blade.php
<table cellspacing="0" cellpadding="10" border="1">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<livewire:user-row :key="$user->id" :user="$user" />
@endforeach
</tbody>
</table>- open the controller for
UserRow.php
<?php
namespace App\Livewire;
use Livewire\Component;
class UserRow extends Component
{
public $user;
public function mount($user){
$this->user = $user;
}
public function archive(){
$this->user;
}
public function render()
{
return view('livewire.user-row');
}
}- open the view for
user-row.php
<tr @class(['archive'=>$user->is_archive])>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td>
@unless ($user->is_archive)
<button type="button" wire:click="archive" wire:confirm="Are you sure want to Archive this??">Archive</button>
@endunless
<button type="button" wire:click="$parent.delete({{ $user->id }})" wire:confirm="Are you sure want to delete this??">Delete</button></td>
</tr>Before we tie up this series, let's see how we can turn our simple Laravel application into an "SPA" (Single page application) by adding the wire:navigate directive to our navigation links.
<nav>
<a wire:navigate href="/todos" @class(['current'=>request()->is('/')])>Todos</a>
<a wire:navigate href="/" @class(['current'=>request()->is('counter')])>Clicker</a>public function save(){
$this->validate();
User::create([
'name'=>$this->name,
'email'=>$this->email,
'password'=>$this->password
]);
$this->redirect('/todos',navigate:true);
}