You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:07:56