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

Laravel10分页时如何保留第一页选中的演员状态?

解决Laravel分页时复选框选中状态丢失的问题

这个问题核心是HTTP的无状态特性导致的——切换分页会刷新页面,前一页的选中状态无法自动保留。下面提供两种实用解决方案:

方案一:前端本地存储(推荐,实现简单)

利用localStorage保存选中的演员ID,页面加载时恢复状态,提交表单时将所有选中ID统一提交。

修改Blade模板

@extends('layouts.app')

@section('title', 'All Casts')

@section('content')
<form action="{{ route('movie.storeCasts', $movie->id) }}" method="post" id="cast-form">
    @csrf
    @method('PATCH')
    <!-- 隐藏字段,用于提交所有选中的ID -->
    <input type="hidden" name="all_selected_casts" id="all-selected-casts">

    <div class="row row-cols-4 g-4 mb-5">
        @forelse ($all_casts as $cast)
            <div class="col">
                <div class="card h-100">
                    <img src="{{ $cast->avatar }}" class="card-img-top" alt="{{ $cast->name }}" style="height: 200px; object-fit:cover;">
                    <div class="card-body">
                        <h5 class="card-title">{{ $cast->name }}</h5>
                        <p class="card-text text-secondary">From <strong>{{ $cast->country }}</strong></p>
                        
                        <div class="mb-3 d-flex">
                            <input type="checkbox" name="cast[]" id="selected_casts_{{ $cast->id }}" class="form-check me-2 cast-checkbox" value="{{ $cast->id }}"
                                    {{ $movie->movieCast->contains('cast_id', $cast->id) ? 'checked' : '' }}>
                            <label for="selected_casts_{{ $cast->id }}" class="form-input">Checked</label>
                        </div>
                    </div>
                </div>
            </div>
        @empty
            <p class="text-center lead text-white">No Casts yet.</p>
        @endforelse
    </div>

    <button type="submit" class="btn btn-primary">保存选中演员</button>
</form>

<div class="d-flex justify-content-center">
    {{ $all_casts->links() }}
</div>

@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
    const movieId = "{{ $movie->id }}";
    const storageKey = `selected_casts_movie_${movieId}`;
    const checkboxes = document.querySelectorAll('.cast-checkbox');
    const hiddenInput = document.getElementById('all-selected-casts');

    // 从localStorage加载已选ID,合并数据库中已保存的ID
    let selectedIds = JSON.parse(localStorage.getItem(storageKey)) || [];
    const savedIds = {!! json_encode($movie->movieCast->pluck('cast_id')->toArray()) !!};
    selectedIds = [...new Set([...selectedIds, ...savedIds])];

    // 恢复复选框选中状态
    checkboxes.forEach(checkbox => {
        if (selectedIds.includes(parseInt(checkbox.value))) {
            checkbox.checked = true;
        }
    });

    // 监听复选框变化,更新localStorage
    checkboxes.forEach(checkbox => {
        checkbox.addEventListener('change', function() {
            const id = parseInt(this.value);
            if (this.checked) {
                !selectedIds.includes(id) && selectedIds.push(id);
            } else {
                selectedIds = selectedIds.filter(item => item !== id);
            }
            localStorage.setItem(storageKey, JSON.stringify(selectedIds));
        });
    });

    // 提交表单时,将所有选中ID存入隐藏字段
    document.getElementById('cast-form').addEventListener('submit', function(e) {
        hiddenInput.value = JSON.stringify(selectedIds);
        // 禁用原复选框,避免重复提交
        document.querySelectorAll('input[name="cast[]"]').forEach(input => input.disabled = true);
    });
});
</script>
@endpush
@endsection

修改控制器代码

public function storeCasts(Request $request, $movie_id) {
    $movie = $this->movie->findOrFail($movie_id);

    $movie->movieCast()->delete();

    // 从隐藏字段获取所有选中的ID
    if($request->all_selected_casts) {
        $selected_casts = [];
        $castIds = json_decode($request->all_selected_casts, true);
        foreach($castIds as $cast_id) {
            $selected_casts[] = ['cast_id' => $cast_id];
        }
        $movie->movieCast()->createMany($selected_casts);
    }
    
    // 提交后清空localStorage,避免下次进入残留状态
    return redirect()->route('movie.edit', $movie_id)->with('success', '演员已更新');
}

方案二:后端Session存储

若不想依赖前端存储,可通过Laravel的Session保存选中状态,分页时自动同步。

修改列表页控制器方法

public function editCasts($movie_id) {
    $movie = $this->movie->findOrFail($movie_id);
    $all_casts = Cast::paginate(12); // 你的分页逻辑
    
    // 从Session获取已选ID,合并数据库已保存的ID
    $selectedIds = session()->get("selected_casts_movie_{$movie_id}", []);
    $savedIds = $movie->movieCast->pluck('cast_id')->toArray();
    $selectedIds = [...new Set([...selectedIds, ...savedIds])];
    
    return view('your-view-path', compact('movie', 'all_casts', 'selectedIds'));
}

修改Blade复选框判断

<input type="checkbox" name="cast[]" id="selected_casts_{{ $cast->id }}" class="form-check me-2" value="{{ $cast->id }}"
        {{ in_array($cast->id, $selectedIds) ? 'checked' : '' }}>

添加AJAX路由与控制器方法

路由:

Route::post('/movie/{movie_id}/cast-toggle', [YourController::class, 'toggleCastSelection'])->name('movie.cast.toggle');

控制器方法:

public function toggleCastSelection(Request $request, $movie_id) {
    $castId = $request->cast_id;
    $storageKey = "selected_casts_movie_{$movie_id}";
    
    $selectedIds = session()->get($storageKey, []);
    
    if($request->checked) {
        !in_array($castId, $selectedIds) && $selectedIds[] = $castId;
    } else {
        $selectedIds = array_filter($selectedIds, fn($id) => $id != $castId);
    }
    
    session()->put($storageKey, $selectedIds);
    
    return response()->json(['success' => true]);
}

添加Blade的AJAX监听

@push('scripts')
<script>
document.querySelectorAll('.cast-checkbox').forEach(checkbox => {
    checkbox.addEventListener('change', function() {
        fetch("{{ route('movie.cast.toggle', $movie->id) }}", {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': "{{ csrf_token() }}"
            },
            body: JSON.stringify({
                cast_id: parseInt(this.value),
                checked: this.checked
            })
        });
    });
});
</script>
@endpush

最终提交控制器代码

public function storeCasts(Request $request, $movie_id) {
    $movie = $this->movie->findOrFail($movie_id);
    $storageKey = "selected_casts_movie_{$movie_id}";
    
    $movie->movieCast()->delete();

    $selectedIds = session()->get($storageKey, []);
    // 合并当前页提交的选中ID,兼容AJAX失效场景
    if($request->cast) {
        $selectedIds = [...new Set([...$selectedIds, ...$request->cast])];
    }
    
    if(!empty($selectedIds)) {
        $selected_casts = array_map(fn($id) => ['cast_id' => $id], $selectedIds);
        $movie->movieCast()->createMany($selected_casts);
    }
    
    // 清空Session
    session()->forget($storageKey);
    
    return redirect()->route('movie.edit', $movie_id);
}

两种方案中,前端本地存储实现成本更低,适合大多数场景;后端Session存储则更适合需要严格控制状态、不信任前端存储的场景。

内容的提问来源于stack exchange,提问作者Yuto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:34:55