Reputation: 2025
Let's say I have CountrySelect
and CitySelect
, where in CountrySelect
are listed all countries and in CitySelect
are only listed those cities of currently selected country...
What would the proper way to pass newly selected country. As I have read in vuejs documentation:
In Vue, the parent-child component relationship can be summarized as props down, events up. The parent passes data down to the child via props, and the child sends messages to the parent via events. Let’s see how they work next.
So in this case, I would detect change of select box inside of CountrySelect
and fire event changed
and along with it a country object. Then the parent component of CountrySelect
would listen for this event and then when that happens will update its attribute country
. Attribute country
is "observed" in parent component and changing its value will cause for DOM to update, so HTML attribute on <city-select>
tag called country
would change. But then how would I detect property change of CitySelect
component, since I need to reload cities via AJAX. I think of putting country
property in watch
object in CitySelect
but that does not seems like an elegant solution to me, it doesn't feels quire right to do...
<template>
<parent>
<country-select name="country_id" v-model="country"></country-select>
<city-select name="city_id" :country="country" v-model="city"></city-select>
</parent>
<template>
<script>
export default {
data: function() {
return {
country: null,
city: null,
};
}
}
</script>
Other way around that I think is if do something like this in parent:
this.$refs.citySelect.emit('countryChanged', this.country)
or:
this.$refs.citySelect.countryChanged(this.country)
I do not know if these two are possible... So what would be the correct way for CountrySelect
component to tell to its sibling component CitySelect
to update cities list...
Upvotes: 2
Views: 3572
Reputation: 82449
By passing country
as a property to your CitySelect
component, you are already doing what you need to do. When country
changes, the changed value will be passed to the CitySelect
component. You simply need to make sure your CitySelect
component is aware of the changes.
Here is a working example.
console.clear()
// Simulated service for retrieving cities by country
const CityService = {
cities: [
{id: 1, country: "USA", name: "New York"},
{id: 2, country: "USA", name: "San Francisco"},
{id: 3, country: "USA", name: "Boston"},
{id: 4, country: "USA", name: "Chicago"},
{id: 5, country: "England", name: "London"},
{id: 6, country: "England", name: "Bristol"},
{id: 7, country: "England", name: "Manchester"},
{id: 8, country: "England", name: "Leeds"},
],
getCities(country){
return new Promise((resolve, reject) => {
setTimeout(() => resolve(this.cities.filter(city => city.country === country)), 250)
})
}
}
Vue.component("CountrySelect",{
props: ["value"],
template: `
<select v-model="selectedCountry">
<option v-for="country in countries" :value="country">{{country}}</option>
</select>
`,
data(){
return {
countries: ["USA", "England"]
}
},
computed:{
selectedCountry:{
get(){return this.value},
set(v){this.$emit('input', v)}
}
}
})
Vue.component("CitySelect", {
props: ["value", "country"],
template: `
<select v-model="selectedCity">
<option v-for="city in cities" :value="city">{{city.name}}</option>
</select>
`,
data(){
return {
cities: []
}
},
computed:{
selectedCity:{
get(){return this.value},
set(v){this.$emit('input', v)}
}
},
watch:{
country(newCountry){
// simulate an AJAX call to an external service
CityService.getCities(newCountry).then(cities => this.cities = cities)
}
}
})
new Vue({
el: "#app",
data:{
country: null,
city: null
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<country-select v-model="country"></country-select>
<city-select :country="country" v-model="city"></city-select>
<hr>
Selected Country: {{country}} <br>
Selected City: {{city}}
</div>
Upvotes: 5