如何结合Data Attributes、SWAL、AJAX与Laravel提交表单?
我有一个带有delete-modal-trigger类的表单,提交时会触发事件监听器。表单包含两个data-*属性:JSON格式的object,以及用于确定调用控制器的type。
JS代码(独立的app.js文件)
$('.delete-modal-trigger').on('submit', async (event) => { event.preventDefault(); let object = JSON.parse(event.target.getAttribute('data-object')); let type = event.target.getAttribute('data-type'); let question = await Swal.fire({ title: `Delete ${object['text']}?`, text: "You won't be able to revert this action!", icon: 'question', showCancelButton: true, confirmButtonColor: '#FF6666', cancelButtonColor: '#3085d6', confirmButtonText: 'DELETE' }); let response = function (alert) { if (alert.isConfirmed) { var url = '{{ route(":type.destroy", :id) }}'; url = url.replace(':type', type); url = url.replace(':id', object['id']); console.log(url); $.ajax({ type: 'POST', url: url, data: { _token: "{{ csrf_token() }}" }, }); } }; response(question); });
表单代码(Blade模板)
<div> <form class="delete-modal-trigger" data-object="{{ $object }}" data-type="tag"> @csrf <x-delete-button /> </form> </div>
提交表单时出现404错误,请求地址为POST http://localhost:8000/admin/%7B%7B%20route(%22tag%22.destroy%22,%204)%20%7D%7D。
示例场景:若type为post、object["id"]为1,期望生成{{ route("post.destroy", 1) }}以对应控制器的destroy方法;删除标签则对应tag.destroy。
已知JavaScript无法解析Blade的route函数,不想将JS代码放在Blade模板内,也不想为每个表单添加data-url(虽可行但不够优雅),请问最佳实践是什么?
方法1:全局注册路由模板映射
在主布局Blade文件(如layouts/app.blade.php)中输出全局JS对象,存储每种type对应的destroy路由模板(用占位符代替ID):
<script> window.routeTemplates = { tag: "{{ route('tag.destroy', '__ID__') }}", post: "{{ route('post.destroy', '__ID__') }}", // 其他需要删除操作的资源类型都在这里添加 }; window.csrfToken = "{{ csrf_token() }}"; </script>
然后在独立的app.js中直接调用全局对象替换占位符:
$('.delete-modal-trigger').on('submit', async (event) => { event.preventDefault(); let object = JSON.parse(event.target.getAttribute('data-object')); let type = event.target.getAttribute('data-type'); let question = await Swal.fire({ title: `Delete ${object['text']}?`, text: "You won't be able to revert this action!", icon: 'question', showCancelButton: true, confirmButtonColor: '#FF6666', cancelButtonColor: '#3085d6', confirmButtonText: 'DELETE' }); if (question.isConfirmed) { let url = window.routeTemplates[type].replace('__ID__', object['id']); $.ajax({ type: 'POST', url: url, data: { _token: window.csrfToken, _method: 'DELETE' // Laravel资源路由destroy方法需要DELETE请求,POST需带该参数 }, success: function() { // 成功后操作:如刷新页面或移除目标元素 location.reload(); } }); } });
这种方式既保持JS代码独立性,又利用Blade生成合法路由前缀,无需给每个表单加data-url。
方法2:按路由规则直接拼接URL
如果你的资源路由URL结构统一(如/admin/{type}/{id}),可直接在JS中拼接URL:
先在布局文件输出前缀:
<script> window.adminPrefix = "{{ url('admin') }}"; window.csrfToken = "{{ csrf_token() }}"; </script>
app.js中构造URL:
$('.delete-modal-trigger').on('submit', async (event) => { event.preventDefault(); let object = JSON.parse(event.target.getAttribute('data-object')); let type = event.target.getAttribute('data-type'); let question = await Swal.fire({ title: `Delete ${object['text']}?`, text: "You won't be able to revert this action!", icon: 'question', showCancelButton: true, confirmButtonColor: '#FF6666', cancelButtonColor: '#3085d6', confirmButtonText: 'DELETE' }); if (question.isConfirmed) { let url = `${window.adminPrefix}/${type}/${object['id']}`; $.ajax({ type: 'POST', url: url, data: { _token: window.csrfToken, _method: 'DELETE' }, success: function() { location.reload(); } }); } });
注意:必须携带_method: 'DELETE'参数,否则Laravel会将POST请求匹配到store方法,导致404。
方法3:统一删除API接口(进阶)
设置一个全局删除接口,由后端根据type和id转发到对应控制器:
后端路由:
Route::post('/admin/delete', [DeleteController::class, 'handle']);
控制器代码:
namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; class DeleteController extends Controller { public function handle(Request $request) { $validated = $request->validate([ 'type' => 'required|string', 'id' => 'required|integer', ]); $routeName = "{$validated['type']}.destroy"; if (!Route::has($routeName)) { abort(404); } $controllerAction = Route::getRoutes()->getByName($routeName)->getAction(); $controller = app($controllerAction['controller']); return $controller->callAction('destroy', [$validated['id']]); } }
app.js中请求统一接口:
$('.delete-modal-trigger').on('submit', async (event) => { event.preventDefault(); let object = JSON.parse(event.target.getAttribute('data-object')); let type = event.target.getAttribute('data-type'); let question = await Swal.fire({ title: `Delete ${object['text']}?`, text: "You won't be able to revert this action!", icon: 'question', showCancelButton: true, confirmButtonColor: '#FF6666', cancelButtonColor: '#3085d6', confirmButtonText: 'DELETE' }); if (question.isConfirmed) { $.ajax({ type: 'POST', url: `${window.adminPrefix}/delete`, data: { _token: window.csrfToken, type: type, id: object['id'] }, success: function() { location.reload(); } }); } });
这种方式适合大量资源类型的删除场景,无需维护路由模板映射,但需额外编写转发控制器。
内容的提问来源于stack exchange,提问作者user13698713

