Livewire Vs Vue: Dependent Dropdowns Demo

Code for Livewire

php artisan make:livewire Companies

 

Now,
Livewire/Companies.php 

open file, and put that code

<?php

namespace App\Http\Livewire;

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

class Companies extends Component
{
    public $countries;
    public $cities;

    public $name;
    public $country;
    public $city;

    public function mount()
    {
        $this->countries = Country::all();
        $this->cities = collect();
    }

    public function render()
    {
        return view('livewire.companies', [
            'companies' => Company::with('city.country')->latest()->take(5)->get()
        ]);
    }

    public function updatedCountry($value)
    {
        $this->cities = City::where('country_id', $value)->get();
    }

    public function storeCompany()
    {
        $this->validate([
            'name' => 'required',
            'city' => 'required',
        ]);

        Company::create([
            'name' => $this->name,
            'city_id' => $this->city,
        ]);

        $this->name = '';
        $this->country = '';
        $this->city = '';
        $this->cities = collect();
    }
}
Furthermore, Let's set up a view,
Here is your layout file,

resources/views/livewire.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Parent-Child Dependent Dropdowns</title>

    <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">

    @livewireStyles
</head>
<body class="bg-gray-100">
<div class="font-sans text-gray-900 antialiased">
    <div class="flex flex-col sm:justify-center items-center pt-5 pb-5">
        <h2 class="font-bold text-2xl">Parent-Child Dependent Dropdowns: Livewire Version</h2>

        <div class="w-full sm:max-w-xl mt-6 mb-6 px-6 py-8 bg-white shadow-md overflow-hidden sm:rounded-lg">
            <div class="mb-4 px-4 py-3 leading-normal text-blue-700 bg-blue-100 rounded-lg" role="alert">
                Fill in the form. Choose the country, and cities list will be updated.
            </div>
            @livewire('companies')
        </div>

        <a href="{{ route('vue') }}" class="mb-4">See Vue.js version</a>
    </div>
</div>
@livewireScripts

</body>
</html>


resources/views/livewire/companies.blade.php

Place that code into your file:
 

<form wire:submit.prevent="storeCompany">
    <div class="mt-4">
        <label class="block font-medium text-sm text-gray-700" for="country">
            Country*
        </label>
        <select wire:model="country" name="country"
                class="mt-2 text-sm sm:text-base pl-2 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400" required>
            <option value="">-- choose country --</option>
            @foreach ($countries as $country)
                <option value="{{ $country->id }}">{{ $country->name }}</option>
            @endforeach
        </select>
    </div>

    <div class="mt-4">
        <label class="block font-medium text-sm text-gray-700" for="city">
            City*
        </label>

        <select wire:model="city" name="city"
                class="mt-2 text-sm sm:text-base pl-2 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400" required>
            @if ($cities->count() == 0)
                <option value="">-- choose country first --</option>
            @endif
            @foreach ($cities as $city)
                <option value="{{ $city->id }}">{{ $city->name }}</option>
            @endforeach
        </select>
    </div>
</form>

web.php

Route::view('/', 'livewire')->name('livewire');

Code for Vue.js

View set up :

resources/views/vue.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Parent-Child Dependent Dropdowns</title>

    <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
<div class="font-sans text-gray-900 antialiased">
    <div class="flex flex-col sm:justify-center items-center pt-5 pb-5">
        <h2 class="font-bold text-2xl">Parent-Child Dependent Dropdowns: Vue.js</h2>

        <div id="app" class="w-full sm:max-w-xl mt-6 mb-6 px-6 py-8 bg-white shadow-md overflow-hidden sm:rounded-lg">
            <div class="mb-4 px-4 py-3 leading-normal text-blue-700 bg-blue-100 rounded-lg" role="alert">
                Fill in the form. Choose the country, and cities list will be updated.
            </div>
            <country-city />
        </div>

        <a href="{{ route('livewire') }}" class="mb-4">See Livewire version</a>
    </div>
</div>

<script src="{{ mix('js/app.js') }}"></script>

</body>
</html>

Component set up:

resources/js/components/CountryCity.vue

<template>
    <div>
        <div class="mt-4">
            <label
                class="block font-medium text-sm text-gray-700"
                for="country"
            >
                Country*
            </label>
            <select
                name="country"
                class="mt-2 text-sm sm:text-base pl-2 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400"
                required
                v-model="selectedCountry"
                v-on:change="getCities(selectedCountry.id)"
            >
                <option value="">-- choose country --</option>
                <option
                    v-for="country in countries"
                    :value="country"
                    v-bind:key="country.id"
                    >{{ country.name }}</option
                >
            </select>
        </div>

        <div class="mt-4">
            <label
                class="block font-medium text-sm text-gray-700"
                for="country"
            >
                City*
            </label>
            <select
                name="city"
                class="mt-2 text-sm sm:text-base pl-2 pr-4 rounded-lg border border-gray-400 w-full py-2 focus:outline-none focus:border-blue-400"
                required
                v-model="selectedCity"
            >
                <option value="" v-if="cities.length == 0">-- choose country first --</option>
                <option
                    v-for="city in cities"
                    :value="city"
                    v-bind:key="city.id"
                    >{{ city.name }}</option
                >
            </select>
        </div>
    </div>
</template>

<script>
import axios from "axios";

export default {
    data() {
        return {
            countries: [],
            cities: [],
            selectedCity: "",
            selectedCountry: ""
        };
    },
    mounted() {
        axios
            .get("/api/countries")
            .then(response => (this.countries = response.data.data));
    },
    methods: {
        getCities(countryId) {
            axios
                .get("api/countries/" + countryId + "/cities")
                .then(response => (
                    (this.cities = response.data.data),
                    (this.selectedCity = this.cities[0])
                ));
        }
    }
};
</script>

Api set up :

routes/api.php

<?php

Route::get('countries', CountriesController::class);
Route::resource('countries.cities', CitiesController::class);

 

For routes :

routes/web.php

<?php

Route::view('/vue', 'vue')->name('vue');

 

Controllers:
 

app/Http/Controllers/CitiesController.php

<?php

namespace App\Http\Controllers;

use App\Models\City;
use App\Models\Country;
use Illuminate\Http\Request;
use App\Http\Resources\CitiesResource;

class CitiesController extends Controller
{
    public function index(Country $country)
    {
        return CitiesResource::collection(
            City::where('country_id', $country->id)->get()
        );
    }
}

Country:

app/Http/Controllers/CountriesController.php

<?php

namespace App\Http\Controllers;

use App\Models\Country;
use Illuminate\Http\Request;
use App\Http\Resources\CountriesResource;

class CountriesController extends Controller
{
    public function __invoke()
    {
        return CountriesResource::collection(Country::all());
    }
}

 

Code Managed for model 

app/Models/City.php

public function country()

{

return $this->belongsTo(Country::class, 'country_id');

}


app/Models/Company.php

<?php


namespace App\Models;


use Illuminate\Database\Eloquent\Factories\HasFactory;

use Illuminate\Database\Eloquent\Model;


class Company extends Model

{

use HasFactory;


protected $fillable = ['name', 'city_id'];


public function city()

{

return $this->belongsTo(City::class);

}

}


app/Models/Country.php

<?php


namespace App\Models;


use Illuminate\Database\Eloquent\Factories\HasFactory;

use Illuminate\Database\Eloquent\Model;


class Country extends Model

{

use HasFactory;


protected $fillable = ['name'];


public function cities()

{

return $this->hasMany(City::class, 'country_id');

}

}

Happy Coding!!

Source : livewire-vs-vuejs-practical-example-of-dependent-dropdowns