Laravel实现DataTable表格按年份Ajax筛选的代码问题排查
现有代码错误点排查
- 筛选下拉逻辑错误:下拉选项遍历全量业务数据生成,option值绑定数据ID、显示完整日期字段,既没有提取去重的独立年份列表,传递的ID参数也无法用于年份匹配
- DataTable渲染模式冲突:开启Ajax配置时没有移除Blade中写死的静态
<tbody>内容,Ajax模式下DataTable会完全接管表格内容渲染,静态行和Ajax返回内容混杂会直接导致排版错乱、重复行 - 请求逻辑缺失:注释代码中的筛选参数仅在页面初始化时取值一次,不会跟随下拉选择更新;Laravel默认POST请求需要校验CSRF令牌,未携带令牌会直接返回419错误
- 列配置缺失:开启Ajax模式时未配置
columns参数指定每列对应的数据字段,DataTable无法正确匹配渲染内容 - 事件绑定失效:删除按钮的点击事件仅在页面初始加载时绑定,Ajax重载生成的新行不会绑定事件,点击无响应
可行实现方案
方案一:修复现有Ajax+DataTable无刷新筛选(支持大数据量)
注意:使用该方案需要先安装DataTable官方Laravel扩展包,执行命令
composer require yajra/laravel-datatables-oracle即可。
- Blade模板修正
首先在页面<head>区域加入CSRF标签:
替换筛选下拉框的生成逻辑,使用去重的年份列表作为选项:<meta name="csrf-token" content="{{ csrf_token() }}">
清空表格静态<div class="col-md-2"> <select id="filter-tahun" class="form-control filter"> <option value="">Pilih Tahun</option> @foreach ($years as $year) <option value="{{$year}}">{{$year}}</option> @endforeach </select> </div><tbody>内容,Ajax模式下由DataTable自动渲染行:<table id="tabelpekerjaan" class="table table-bordered"> <thead> <tr> <th style="width: 10px">No.</th> <th>Paket Pekerjaan</th> <th>Tanggal</th> <th>Nama Perusahaan</th> <th>Lokasi Pekerjaan</th> <th>PPK</th> <th>HPS</th> <th>Gambar</th> <th style="width: 120px">Aksi</th> </tr> </thead> <tbody></tbody> </table> - 后端逻辑修正
新增Ajax接口路由:
控制器中新增接口方法,处理筛选、返回DataTable要求的JSON格式数据:// routes/web.php Route::post('/datapekerjaan', [PekerjaanController::class, 'dataPekerjaan']);
原页面渲染方法中传递去重的年份列表:// app/Http/Controllers/PekerjaanController.php public function dataPekerjaan(Request $request) { $data = Pekerjaan::with(['penyedia', 'user']) ->when($request->tahun, function($query) use ($request) { // 按年份匹配日期字段 $query->whereYear('tanggal', $request->tahun); }) ->get(); return datatables()->of($data) ->addIndexColumn() // 自动生成序号列 ->editColumn('hps', function($row) { return 'Rp. '.number_format($row->hps,0,',',','); }) ->editColumn('gambar', function($row) { return '<img src="'.asset('gambarpekerjaan/'.$row->gambar).'" style="width: 100px" alt="">'; }) ->addColumn('aksi', function($row) { $btn = '<a href="/editpekerjaan/'.$row->id.'" type="button" class="btn btn-outline-primary">Edit</a>'; if (auth()->user()->status == 'super') { $btn .= '<a href="#" type="button" class="btn btn-outline-danger delete" data-id="'.$row->id.'" data-nama="'.$row->pekerjaan.'">Hapus</a>'; } return $btn; }) ->rawColumns(['gambar', 'aksi']) // 声明HTML列不转义 ->make(true); }public function index() { $years = Pekerjaan::selectRaw('YEAR(tanggal) as year')->distinct()->pluck('year'); return view('对应视图路径', compact('years')); } - 前端脚本修正
替换原DataTable初始化逻辑,补全参数配置、事件委托:<script> $(function () { // 全局自动携带CSRF令牌 $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); const table = $('#tabelpekerjaan').DataTable({ "paging": true, "lengthChange": true, "searching": true, "ordering": true, "info": true, "autoWidth": true, "responsive": true, "processing": true, "serverSide": true, "ajax":{ url: "{{url('/datapekerjaan')}}", type:"POST", data:function(d){ d.tahun = $("#filter-tahun").val(); // 实时读取选中的年份 } }, "columns": [ {"data": "DT_RowIndex", "orderable": false, "searchable": false}, {"data": "pekerjaan"}, {"data": "tanggal"}, {"data": "penyedia.nama"}, {"data": "lokasi"}, {"data": "user.name"}, {"data": "hps"}, {"data": "gambar"}, {"data": "aksi", "orderable": false, "searchable": false} ] }); // 选择年份后重载表格 $(".filter").on('change',function(){ table.ajax.reload(null,false); }) // 用事件委托绑定删除事件,兼容Ajax生成的新元素 $(document).on('click', '.delete', function(){ var idpekerjaan = $(this).attr('data-id'); var namapekerjaan = $(this).attr('data-nama'); Swal.fire({ title: 'Apakah Anda yakin?', text: "Paket Pekerjaan "+namapekerjaan+" akan dihapus!", icon: 'warning', showCancelButton: true, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'Ya, yakin!' }).then((result) => { if (result.isConfirmed) { window.location = "/deletepekerjaan/"+idpekerjaan Swal.fire( 'Dihapus!', 'Data berhasil dihapus.', 'success' ) } }); }); $('.select2bs4').select2({ theme: 'bootstrap4' }) }); @if (session()->has('message')) toastr.success("{{session()->get('message')}}") @endif </script>
方案二:轻量表单筛选(代码改动最小,适合千条以内数据量)
不需要写Ajax接口,直接用GET传参实现筛选,保留原有静态表格渲染逻辑:
- 把筛选下拉框包裹在表单中,选中后自动提交:
<form method="get"> <div class="col-md-2"> <select id="filter-tahun" name="tahun" class="form-control filter" onchange="this.form.submit()"> <option value="">Pilih Tahun</option> @foreach ($years as $year) <option value="{{$year}}" {{request('tahun') == $year ? 'selected' : ''}}>{{$year}}</option> @endforeach </select> </div> </form> - 原页面渲染方法中加入筛选逻辑即可:
public function index(Request $request) { $years = Pekerjaan::selectRaw('YEAR(tanggal) as year')->distinct()->pluck('year'); $pekerjaan = Pekerjaan::with(['penyedia', 'user']) ->when($request->tahun, function($query) use ($request) { $query->whereYear('tanggal', $request->tahun); }) ->get(); return view('对应视图路径', compact('pekerjaan', 'years')); }
该方案选中选项后页面会自动刷新,展示匹配年份的数据,无需额外编写JS逻辑,出错概率极低。
内容的提问来源于stack exchange,提问作者YuPi
相关产品推荐
相关产品推荐

