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

如何用Livewire实现嵌套组件向父组件传参并解决房间保存问题?

问题分析与解决方案

问题概述

使用Livewire搭建房屋与房间的增编系统时,房屋信息可正常保存,但房间数据无法同步保存,尝试通过$emitUp传递房间参数至父组件的操作未生效。

核心问题点

  1. Room视图参数传递错误:使用模板插值{{ $id }}/{{ $name }}传递参数,仅能渲染初始值,无法获取输入框更新后的动态值。
  2. 父组件属性缺失:Houses组件未定义$rooms(存储房间数据)和$house_id(当前操作房屋ID)属性,导致存储逻辑无法执行。
  3. 房间存储逻辑低效:原代码直接删除所有房间再重建,可优化为仅清理未保留的房间。

修正后的代码实现

1. RoomShow组件(子组件)

// app/Http/Livewire/RoomShow.php
namespace App\Http\Livewire;

use Livewire\Component;

class RoomShow extends Component
{
    public $room;

    public function mount($room)
    {
        $this->room = $room;
    }

    public function render()
    {
        return view('livewire.rooms.room');
    }
}

2. Room视图文件

{{-- views/livewire/rooms/room.blade.php --}}
<div>
    <input type="text" wire:model="room.name" placeholder="房间名称" 
           wire:change="$emitUp('roomUpdated', room.id, room.name)">
</div>

3. Houses组件(父组件)

// app/Http/Livewire/Houses.php
namespace App\Http\Livewire;

use App\Models\House;
use App\Models\Room;
use Livewire\Component;

class Houses extends Component
{
    public $houses, $address1, $town, $house_id;
    public $isOpen = 0;
    public $rooms = [];
    protected $listeners = ['roomUpdated' => 'updateRoom'];
    
    public function render()
    {
        $this->houses = House::all();
        return view('livewire.houses.houses');
    }

    // 打开创建/编辑模态框
    public function create()
    {
        $this->resetInputFields();
        $this->openModal();
    }

    // 加载编辑数据
    public function edit($id)
    {
        $house = House::findOrFail($id);
        $this->house_id = $id;
        $this->address1 = $house->address1;
        $this->town = $house->town;
        $this->rooms = $house->rooms->toArray();
        $this->openModal();
    }

    public function openModal()
    {
        $this->isOpen = true;
    }

    public function closeModal()
    {
        $this->isOpen = false;
    }

    // 重置表单字段
    private function resetInputFields()
    {
        $this->house_id = null;
        $this->address1 = '';
        $this->town = '';
        $this->rooms = [];
    }

    // 添加新房间
    public function addRoom()
    {
        $this->rooms[] = ['id' => null, 'name' => ''];
    }

    // 更新房间数据
    public function updateRoom($roomId, $roomName)
    {
        foreach ($this->rooms as &$room) {
            if ($room['id'] == $roomId) {
                $room['name'] = $roomName;
                break;
            }
        }
    }

    public function store()
    {
        // 数据验证
        $this->validate([
            'address1' => 'required',
            'town' => 'required',
        ]);

        // 创建/更新房屋
        $house = House::updateOrCreate(
            ['id' => $this->house_id],
            ['address1' => $this->address1, 'town' => $this->town]
        );

        // 处理房间:删除未保留的房间
        if ($this->house_id) {
            $existingRoomIds = collect($this->rooms)->pluck('id')->filter()->toArray();
            Room::where('house_id', $house->id)->whereNotIn('id', $existingRoomIds)->delete();
        }

        // 创建/更新房间
        foreach($this->rooms as $room){
            if (!empty($room['name'])) {
                Room::updateOrCreate(
                    ['id' => $room['id']],
                    ['house_id' => $house->id, 'name' => $room['name']]
                ); 
            }
        }

        session()->flash('message', $this->house_id ? '房屋更新成功。' : '房屋创建成功。');
        $this->closeModal();
        $this->resetInputFields();
    }
}

4. createHouse视图文件

{{-- views/livewire/houses/createHouse.blade.php --}}
<div role="dialog" aria-modal="true" aria-labelledby="modal-headline" @if(!$isOpen) style="display:none;" @endif>
    <form wire:submit.prevent="store">
        <div class="w-1/2 pr-4">
            <div class="mb-4">
                <label for="input1">地址1:</label>
                <input type="text" id="input1" placeholder="输入地址" wire:model.defer="address1">
                @error('address1') <span class="text-red-500">{{ $message }}</span> @enderror
            </div>
            <div class="mb-4">
                <label for="input2">城镇:</label>
                <input type="text" id="input2" placeholder="输入城镇" wire:model.defer="town">
                @error('town') <span class="text-red-500">{{ $message }}</span> @enderror
            </div>
        </div>
        <div class="w-1/2">
            <h3 class="mb-2">房间</h3>
            @foreach ($rooms as $key => $room)
                <div class="mb-2">
                    @livewire('room-show', ['room' => $room], key('room-' . $key))
                    <button type="button" wire:click="removeRoom({{ $key }})" class="ml-2 text-red-500">删除</button>
                </div>
            @endforeach
            <button type="button" wire:click="addRoom" class="mb-4 text-blue-500">添加房间</button>
        
            <span>
                <button type="submit">保存</button>
                <button type="button" wire:click="closeModal" class="ml-2">取消</button>
            </span>
        </div>
    </form>
</div>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:47:34