Laravel livewire
- Livewire là một full-stack framework Laravel giúp cho việc xây dựng các dynamic interfaces đơn giản. Nói cách khác là chúng ta có thể build 1 trang web hoàn toàn bằng Laravel mà không cần sử dụng JS.
1. Installation
Chúng ta sẽ cài đặt thông qua composer như sau :
composer require livewire/livewire
Sau khi cài đặt thành công chỉ cần bạn include chúng vào nơi mà bạn cần dùng là được, ví dụ ở đây mình sẽ include vào app.blade.php để toàn bộ ứng dụng của mình sẽ được sử dụng livewire
app.blade.php
...
@livewireStyles
</head><body>
...
@livewireScripts
</body></html>
Tạo 1 component để thử nghiệm :
php artisan make:livewire hello-word
Sau khi chạy lệnh trên, chúng ta thấy nó sẽ sinh ra cho chúng ta 2 files như sau :
AppHttpLivewireHelloWord.php
namespaceAppHttpLivewire;useLivewireComponent;classHelloWorldextendsComponent{publicfunctionrender(){returnview('livewire.hello-word');}}
resourcesviewslivewirehello-word.blade.php
<div><h1>Hello World!</h1> // nội dung ta thêm vào
</div>
và bây giờ chúng ta thử include nó vào file app để xem kết quả :
...
<livewire:hello-word/>
Its works !!
Vậy là chúng ta đã hoàn thành bước đầu tiên trong việc sử dụng livewire.
2. Data binding
Chi tiết: https://laravel-livewire.com/docs/2.x/properties
Khái niệm đầu tiên và quan trọng nhất để hiểu khi sử dụng LiveWire là “data binding”. Đây là phần cốt lõi của Livewire nên các bạn đặc biệt chú trọng với nó.
Khái niệm này rất là đơn giản và dễ hiểu cùng xem vd dưới đây :
classHelloWorldextendsComponent{public$message='Hello World!';}
<div><h1>{{ $message }}</h1><!-- Output sẽ là "Hello World!" --></div>
Data binding theo ví dụ trên có nghĩa là : Một thuộc tính public
của Livewire component sẽ được binding qua view hoặc ngược lại, vậy có nghĩa là khi thuộc tính này được change ở view thì ở component sẽ update theo và ngược lại. Cùng tìm hiểu nhé.
Tại html view ta thêm 1 ô input :
<div><h1>{{ $message }}</h1><inputtype="text"wire:model="message"/></div>
Và thử gõ vào input nào, … Wow $message
cũng được update theo, ơ khoang :v cái này quen quen, đúng rồi, nếu các bạn học qua Vuejs
thì nó chi chang rứa đó :v. Vậy làm sao nó có thể làm được điều đó ? bạn hãy thử bật qua tab network mà xem, mỗi lần bạn type trên input, thì nó sẽ send request kèm input và update lại DOM
, vì thế bạn mới thấy là nó sẽ realtime value.
Có 3 chú ý như sau khi đặt tên property:
- Tên property không thể conflict với tên property khác trong Livewire reserved (vd: rules hay messages).
- Data sẽ được lưu dưới dạng
public
để có thể hiện thị phía front-end javascript, Do đó bạn không nên cho các dữ liệu nhạy cảm vào đây. - VÌ data nó sẽ được mapping với phía js cho nên những property này phải được mapping type với js (string, int, array, boolean) hoặc là với php (Stringable, Collection, DateTime, Model, EloquentCollection).
Binding Directly To Model Properties:
Ngoài ra nó còn support chúng ta binding vào model nếu khai báo nó public
, vd:
useAppPost;classPostFormextendsComponent{publicPost$post;protected$rules=['post.title'=>'required|string|min:6','post.content'=>'required|string|max:500',];publicfunctionsave(){$this->validate();$this->post->save();}}
<formwire:submit.prevent="save"><inputtype="text"wire:model="post.title"><textareawire:model="post.content"></textarea><buttontype="submit">Save</button></form>
Như vậy khi bạn submit, mặc định các data sẽ được set vào model.
** Chú ý ** :
$rules
khai báo các field phải mapping với attr của model. ngoài ra nếu muốn check các rules nâng cao thì làm như sau :
...protected$rules=[]...publicfunctionrules(){$id=$this->post->id??0;'post.name'=>'required|unique:posts,name,'.$id,// vì khi các bạn để rules trên nó sẽ ko hiểu id, và khi các bạn dùng Facade Rule cũng vậy}
3.Actions:
Chúng như các framework FE khác thì livewire cũng có những actions tương tự để tương tác với người dùng, vd như :
Event | Directive |
---|---|
click | wire:click |
keydown | wire:keydown |
submit | wire:submit |
Nhìn thấy quen đúng không? nếu các bạn làm việc với vue rồi thì thằng này y chang như vậy, chỉ khác cách gọi
<buttonwire:click="doSomething">Do Something</button><inputwire:keydown.enter="doSomething"><formwire:submit.prevent="save">
...
<button>Save</button></form>
** Bạn có thể listen cho bất kì event dispatched bởi element mà bạn binding**
<button wire:somethingDispatcher="someAction">
Passing Action Parameters
Bạn cũng có thể truyền các action, param vào
<buttonwire:click="addTodo({{ $todo->id }}, '{{ $todo->name }}')">
Add Todo
</button>
Và ở component ta khai báo actions
publicfunctionaddTodo($id,$name){...}
Còn nhiều actions khác, hãy tham khảo tại đây : https://laravel-livewire.com/docs/2.x/actions
##Lifecircle hooks
Một phần không thể thiếu ở các framework FE, và livewire cũng vậy.
Hooks | Description |
---|---|
mount | Chỉ chạy 1 lần, sau khi components đc khởi tạo và trước khi render() |
hydrate | Chạy mỗi khi có request, thực hiện trước khi exec 1 action hoặc render() |
updating | Chaỵ trước khi prop được update |
updated | Chạy sau khi prop được update |
Ngoài ra còn có thể add thêm biến phía sau updating, updated vd:
hydrateFoo, updatingFoo, updatedFoo => các hooks này chạy và tương tác với biến $foo, nếu biến có 2 từ thì nhận cả 2 vd : $foo_bar hoặc $fooBar.
// Chúng ta sẽ có lifecircle Class như sau :classHelloWorldextendsComponent{public$foo;publicfunctionmount(){}publicfunctionhydrateFoo($value){}publicfunctiondehydrateFoo($value){}publicfunctionhydrate(){}publicfunctiondehydrate(){}publicfunctionupdating($name,$value){}publicfunctionupdated($name,$value){}publicfunctionupdatingFoo($value){}publicfunctionupdatedFoo($value){}publicfunctionupdatingFooBar($value){}publicfunctionupdatedFooBar($value){}}
Javascript Hooks
Ngoài class lifecircle ra thì ở FE của js cũng có lifecircle:
<script>
document.addEventListener("DOMContentLoaded",()=>{
Livewire.hook('component.initialized',(component)=>{})
Livewire.hook('element.initialized',(el, component)=>{})
Livewire.hook('element.updating',(fromEl, toEl, component)=>{})
Livewire.hook('element.updated',(el, component)=>{})
Livewire.hook('element.removed',(el, component)=>{})/* cả đoạn trên chắc hiểu rồi nhỉ ? */
Livewire.hook('message.sent',(message, component)=>{})/*trigger khi update message đến server thông qua Ajax*/
Livewire.hook('message.failed',(message, component)=>{})/* Được gọi nếu message bị failed */
Livewire.hook('message.received',(message, component)=>{})/* Được gọi nếu nhận response, và trước khi update DOM */
Livewire.hook('message.processed',(message, component)=>{})/* Được gọi trước khi Livewire processes all side effects (including DOM-diffing) từ message */});</script>
message là mỗi khi ta tương tác đến phía component nó sẽ call ajax để update lại DOM, cả lượt đi lẫn lượt về gọi là roundtrip.
Ngoài ra có thể tham khảo ở đây: https://laravel-livewire.com/docs/2.x/lifecycle-hooks
Nesting Components
Về phần này thì chắc không cần phải nói, vì chính blade cũng có thể làm được rồi, chỉ có chú ý như sau :
- Nested components có thể nhận data parameters từ parent của nó, Tuy nhiên chúng không reactive như props từ vue component
- Livewire component không nên được dùng cho các trường hợp tách nhỏ như blade files, đối với như này, chúng ta cứ dùng include của blade thì tốt hơn
classUserDashboardextendsComponent{publicUser$user;}
<div><h2>User Details:</h2>
Name: {{ $user->name }}
Email: {{ $user->email }}
<h2>User Notes:</h2><div><!-- nested component -->
@livewire('add-user-note', ['user' => $user])
</div></div>
Events
Một phần rất quan trọng của livewire, cũng như các framework, library khác. Các livewire component có thể giao tiếp với nhau thông qua global event, miễn chúng đang trên cùng 1 trang, chúng có thể giao tiếp bằng các events and listeners.
Firing Events
Có 3 cách để firing events.
<!-- từ template --><buttonwire:click="$emit('postAdded')">
// từ component$this->emit('postAdded');
/* từ global js */<script>
Livewire.emit('postAdded')</script>
Khá là dễ hiểu phải không?. Thì sau khi chúng ta fire một event thì chắc sẽ có listener để nghe nó, và sẽ được define trong component như sau :
classShowPostsextendsComponent{public$postCount;protected$listeners=['postAdded'=>'postAdded'];// vậy mỗi lần postAdded được fire thì action postAdded sẽ được gọi// nếu cả listener và action cùng tên, ta chỉ cần protected $listeners = ['postAdded'];publicfunctionpostAdded(){// handle}// hoặc nếu bạn muốn event được dynamic thì khai báoprotectedfunctiongetListeners(){return['postAdded'=>true?'postAdded':'someAction'];}}
Passing Parameters
Chỉ cần 1 ví dụ là hiểu được ngay , ngắn gọn xúc tích.
$this->emit('postAdded',$post->id);// another componentpublicfunctionpostAdded(Post$post){$this->postCount=Post::count();$this->recentlyAddedPost=$post;}
Scoping Events
Scoping To Parent Listeners
Khi làm việc với nested components, thỉnh thoảng bạn chỉ muốn emit events đén parents và không emit tới children hoặc sibling components.
$this->emitUp('postAdded');
hoặc
<buttonwire:click="$emitUp('postAdded')">
Scoping To Components By Name
Hoặc emit đến tên component chỉ định
$this->emitTo('counter','postAdded');
<buttonwire:click="$emitTo('counter', 'postAdded')">
Scoping To Self
Hoặc chỉ emit chỉnh component này thôi (thường chỉ cách này ta call trực tiếp đến action đó khoẻ hơn :v )
$this->emitSelft('counter','postAdded');
<buttonwire:click="$emitSelft('counter', 'postAdded')">
Ngoài listern ở component ta cũng có thể listen ở js:
Thường chúng ta dùng cái này để show các toarts msg, …
<script>
Livewire.on('postAdded',postId=>{alert('A post was added with the id of: '+ postId);})</script>
Dispatching Browser Events
Livewire cho phép bạn fire browser window event như sau:
$this->dispatchBrowserEvent('name-updated',['newName'=>$value]);
Và bạn sẽ listen nó như sau:
<script>
window.addEventListener('name-updated',event=>{alert('Name updated to: '+ event.detail.newName);})</script>
Khá là dễ phải không ?
KẾT
Bên trên là những thứ cơ bản để có thể làm quen với laravel livewire, ở bài sau, chúng ta sẽ đi vào sâu hơn. Đón xem ở bài sau nhé. Cảm ơn các bạn đã lắng nghe (bow)
Nguồn: viblo.asia