【Laravel實戰】從零開始學會用Livewire寫出可編輯的表格
你是否有個需求是希望能夠更便利的修改資料庫表格的資料,就如同編輯Excel檔案一般。那今天的這個實作教學就是為你所寫的
本實作是在說明如何達成當按下表格的某欄資料後,能夠以inline的方式立即進行修改,就如同下圖那樣

其中點資料進行編輯,也就是Editable的功能是透過X-editable項目來實現,至於Table的佈局是透過Tailwind CSS來實現,詳細資料請看參考文件
Editable實作
Step 1.編輯Livewire組件
建立好組件之後,加入等會視圖所需的公開屬性
/app/Http/Livewire/EditableLink.php
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class EditableLink extends Component
{
public $model; //模型實例
public $pk; //主鍵
public $pk2; //主鍵2,用於複合主鍵,非必須
public $col_key; //要編輯的欄位
public $type = 'text'; //欄位輸入項類型
public $options = []; //選項
public $title = '請填入..'; //popup標題
public $pkObj //主鍵物件,X-editable要用;
public function mount()
{
$data = [$this->pk => $this->model->{$this->pk}];
if(isset($this->pk2)){
$data[$this->pk2] = $this->model->{$this->pk2};
}
$data['table'] = $this->model->getTable();
$this->pkObj = json_encode($data);
}
public function render()
{
return view('livewire.editable-link');
}
}
這個視圖是為了組合出 X-editable 所需要的 Anchor 標籤
//resources/views/livewire/editable-link.blade.php
<a href="#" data-title="{{ $title }}" class="xedit" id="{{ $col_key }}" data-type="{{ $type }}"
data-source="{{ json_encode($options) }}" data-pk="{{ $pkObj }}" >
{{ $model->{$col_key} }}
</a>
Step 2.編輯表格視圖
在每個資料的部分使用先前所建立的組件
組件使用範例如下:
這是單一主鍵的寫法
@livewire('editable-link', ['pk'=>'id','col_key'=>'name','model'=>$member],key('name-'.$member->id))
這是複合主鍵的寫法,需再加上pk2
@livewire('editable-link',['pk'=>'id','pk2'=>'id2','col_key'=>'name','model'=>$member],key('name-'.$member->id))
下面給出一個完整的前端編輯表格呈現頁面範例
//resources/views/livewire/member-table.blade.php
<div>
<div class="flex flex-col">
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
姓名
</th>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
地址&電話
</th>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
手機
</th>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
狀態
</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">刪除</span>
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach ($members as $member)
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10">
<img class="h-10 w-10 rounded-full"
src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60"
alt="">
</div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
@livewire('editable-link',
['pk'=>'id'
,'col_key'=>'name','model'=>$member],key('name-'.$member->id))
</div>
<div class="text-sm text-gray-500">
{{ $member->id }}
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">
@livewire('editable-link',
['pk'=>'id','col_key'=>'addr','type'=>'textarea','model'=>$member],key('addr-'.$member->id))
</div>
<div class="text-sm text-gray-500">
@livewire('editable-link',
['pk'=>'id','col_key'=>'tel','model'=>$member],key('tel-'.$member->id))
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
@livewire('editable-link',
['pk'=>'id','col_key'=>'mobile','model'=>$member],key('mobile-'.$member->id))
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
@livewire('editable-link',
['pk'=>'id','col_key'=>'status','type'=>'select','model'=>$member,'options'=>$statuses],key('status-'.$member->id))
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<a href="#" class="text-indigo-600 hover:text-indigo-900">刪除</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- 請確保app.blade.php裡頭有css這個Stack-->
@push('css')
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
<link href="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/bootstrap3-editable/css/bootstrap-editable.css"
rel="stylesheet" />
@endpush
<!-- 請確保app.blade.php裡頭有js這個Stack-->
@push('js')
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/bootstrap3-editable/js/bootstrap-editable.min.js">
</script>
<script>
//設定編輯模式為inline,預設為popup
$.fn.editable.defaults.mode = 'inline';
function initXedit()
{
$('.xedit').editable({
url: '{{url("api/table/update")}}',
showbuttons: true, //是否要出現確認按鈕
toggle: 'click', //互動方式,其他選項有dbclick.mouseenter
success: function (response, newValue) {
console.log('Updated', response)
}
});
}
//為Editable輸入項加入編輯功能
$(document).ready(function () {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': '{{csrf_token()}}'
}
});
initXedit();
});
</script>
@endpush
編輯表格控制器
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Member;
class MemberTable extends Component
{
//select選單要用的選項
public $statuses = ['1' => '開啟', '2' => '關閉'];
public function render()
{
$members = Member::take(5)->get();
return view('livewire.member-table',compact('members'))->layout('layouts.app');
}
}
編輯Update API
用於等會前端程式呼叫之用
//app/Http/Controllers/ApiController.php
use DB;
use Log;
//支持 X-editable 功能的 API
public function updateTable(Request $request)
{
$pk = $request->input('pk');
//Log::debug($pk);
$pk_keys = array_keys($pk);
$pk_keys = array_diff($pk_keys,['table']);
//Log::debug($pk_keys);
if ($request->ajax()) {
$query = DB::table($pk['table']);
//支持複合主鍵
foreach($pk_keys as $key){
$query = $query->where($key,$pk[$key]);
}
$query->update([$request->input('name') => $request->input('value')]);
return response()->json(['success' => true]);
}
}
編輯API路由
//routes/api.php
Route::post('/table/update', 'App\Http\Controllers\ApiController@updateTable');
分頁實作
安裝 Tailwind CSS
因為 Laravel8 預設的分頁是透過 Tailwind CSS 來實現,所以需要先安裝好 Tailwind CSS
Step 1.安裝前端套件
npm install
Step 2.透過npm來安裝 Tailwind CSS
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
Step 3.建立 Tailwind CSS 設定檔案
npx tailwindcss init
生成好的檔案內容如下:
// tailwind.config.js
module.exports = {
purge: [],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
Step 4.修改設定檔內的purge設定
將之改成如下:
// tailwind.config.js
purge: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.vue',
],
Step 5.在Laravel Mix 設置檔中加入 tailwind CSS
//webpack.mix.js
mix.js("resources/js/app.js", "public/js")
.postCss("resources/css/app.css", "public/css", [
require("tailwindcss"),
]);
Step 6.編輯 app.css 素材檔
在 app.css 加入以下程式碼
//resources/css/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Step 7.在佈局視圖載入 app.css
在 resources/views/layouts/app.blade.php 加入以下內容
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
Step 8.編譯 css/app.css
執行以下指令
npm run dev (開發環境)
npm run production (正式環境)
修改表格視圖
在要呈現分頁連結的位置加入以下程式碼
{{ $hcusts->links() }}
在最後的 script 區域加入以下程式碼
//每次翻頁都要再做initXedit
window.addEventListener('turn-page', event => {
//alert('Name updated to: ' + event.detail.newName);
initXedit()
})
修改表格控制器
在控制器或組件類別裡頭加入分頁相關的程式碼,以下用全頁組件作示範,列出需修改的部分
//app\Http\Livewire\MemberTable.php
use Livewire\WithPagination;
class MemberTable extends Component
{
//啟動分頁功能,預設為Tailwind,需要先安裝
use WithPagination;
...
//每次換頁時都會呼叫這個方法,用來發出事件
public function hydrate()
{
$this->dispatchBrowserEvent('turn-page');
}
//render方法要微調
public function render()
{
//分頁查詢要改呼叫paginate,5為一頁的筆數
$hcusts = Hcust::paginate(5);
return view('livewire.hcust-table',compact('hcusts'))->layout('layouts.app');
}
}



