Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

aarontharker's avatar

Nested component in a form not updating as expected

Let me start by saying I'm just transitioning back to Livewire after a couple of years away so forgive me if I'm asking a stupid question :) I've created a Country/State/City component and for what I'm using it I really need all 3 ids, not just the final city id. I'm aware of Livewire's limitation of only allowing a single modelable attribute so internally in the component I'm adding the ids to a collection and then using that collection for the modelable attribute. The following is a excerpt from my component.

#[Modelable] 
public $countryStateCity;

public function mount()
    {
        $this->countries = Country::orderBy('name')->get();
    }

    public function updatedSelectedCity($cityId)
    {
        $this->updateValue();
    }

    public function updatedSelectedCountry($countryId)
    {
        $this->updateValue();
        $states = State::where('country_id', $countryId)->orderBy('name')->get();
        if (count($states) > 0) {
            $this->states = $states;
        }
    }

    public function updatedSelectedState($stateId)
    {
        $this->updateValue();
        $cities = City::where('state_id', $stateId)->orderBy('name')->get();
        if (count($cities) > 0) {
            $this->cities = $cities;
        }
    }

    public function updateValue()
    {
        $this->countryStateCity = collect([
            'country_id' => $this->selectedCountry,
            'state_id' => $this->selectedState,
            'city_id' => $this->selectedCity
        ]);
    }

In the parent form I've made the component "live" using wire:model.live and added an updated method to the parent form component but this doesn't appear to be working.

public function updatedCountryStateCity ($countryStateCity)
    {
        $this->city_id = $countryStateCity->city;
        $this->state_id = $countryStateCity->state;
        $this->country_id = $countryStateCity->country;
    }

Can anyone suggest what I'm doing wrong?

0 likes
2 replies
LaryAI's avatar
Level 58

It looks like you're on the right track, but there are a few things that might be causing issues with your Livewire component. Let's go through your code and make some adjustments to ensure everything works as expected.

Component Code

  1. Initialization of Selected Variables: Ensure that you initialize the selectedCountry, selectedState, and selectedCity variables in the mount method.
  2. Update Method: Ensure that the updateValue method is called correctly and that the countryStateCity property is updated properly.
  3. Parent Component: Ensure that the parent component is correctly listening for updates from the child component.

Here's a revised version of your component:

use Livewire\Component;
use App\Models\Country;
use App\Models\State;
use App\Models\City;

class CountryStateCity extends Component
{
    public $countries;
    public $states = [];
    public $cities = [];

    public $selectedCountry = null;
    public $selectedState = null;
    public $selectedCity = null;

    #[Modelable]
    public $countryStateCity;

    public function mount()
    {
        $this->countries = Country::orderBy('name')->get();
        $this->updateValue();
    }

    public function updatedSelectedCity($cityId)
    {
        $this->updateValue();
    }

    public function updatedSelectedCountry($countryId)
    {
        $this->updateValue();
        $this->states = State::where('country_id', $countryId)->orderBy('name')->get();
        $this->cities = []; // Reset cities when country changes
    }

    public function updatedSelectedState($stateId)
    {
        $this->updateValue();
        $this->cities = City::where('state_id', $stateId)->orderBy('name')->get();
    }

    public function updateValue()
    {
        $this->countryStateCity = collect([
            'country_id' => $this->selectedCountry,
            'state_id' => $this->selectedState,
            'city_id' => $this->selectedCity
        ]);
    }

    public function render()
    {
        return view('livewire.country-state-city');
    }
}

Parent Component

Ensure that the parent component is correctly listening for updates from the child component. Here's an example of how you might set this up:

use Livewire\Component;

class ParentForm extends Component
{
    public $countryStateCity;
    public $city_id;
    public $state_id;
    public $country_id;

    public function updatedCountryStateCity($countryStateCity)
    {
        $this->city_id = $countryStateCity['city_id'];
        $this->state_id = $countryStateCity['state_id'];
        $this->country_id = $countryStateCity['country_id'];
    }

    public function render()
    {
        return view('livewire.parent-form');
    }
}

Blade Templates

Ensure that your Blade templates are set up correctly. Here’s an example of how you might set up the child component:

<!-- resources/views/livewire/country-state-city.blade.php -->
<div>
    <select wire:model="selectedCountry">
        <option value="">Select Country</option>
        @foreach($countries as $country)
            <option value="{{ $country->id }}">{{ $country->name }}</option>
        @endforeach
    </select>

    <select wire:model="selectedState">
        <option value="">Select State</option>
        @foreach($states as $state)
            <option value="{{ $state->id }}">{{ $state->name }}</option>
        @endforeach
    </select>

    <select wire:model="selectedCity">
        <option value="">Select City</option>
        @foreach($cities as $city)
            <option value="{{ $city->id }}">{{ $city->name }}</option>
        @endforeach
    </select>
</div>

And the parent component:

<!-- resources/views/livewire/parent-form.blade.php -->
<div>
    <livewire:country-state-city wire:model.live="countryStateCity" />

    <div>
        <p>Selected Country ID: {{ $country_id }}</p>
        <p>Selected State ID: {{ $state_id }}</p>
        <p>Selected City ID: {{ $city_id }}</p>
    </div>
</div>

Summary

  1. Ensure that the selectedCountry, selectedState, and selectedCity variables are initialized.
  2. Ensure that the updateValue method is called correctly and updates the countryStateCity property.
  3. Ensure that the parent component is correctly listening for updates from the child component.

With these adjustments, your nested component should update as expected.

aarontharker's avatar

ok all of Lary's suggestions are actually already in place I simply hadn't shared those pieces of the code to keep the length of the post shorter.

Please or to participate in this conversation.