0
The usual solution to the Select All checkbox is to use a computed with a setter. When the box is checked, all the sub-boxes are checked (via the set function). When a sub-box changes, the Select All box value is re-evaluated (in the get function).
Here, we have a twist: if the sub-boxes are mixed, the Select All box should indicate that somehow. The approach is still to use a computed, but instead of just true and false values, it can return a third value.
There’s no built-in way of representing a third value in a checkbox; I’ve chosen to replace it with a yin-yang emoji.
const rawData = {
'North American Countries': {
'countries': {
'us': {
'name': 'United States'
},
'ca': {
'name': 'Canada'
}
}
},
'European Countries': {
countries: {}
}
};
const countryComponent = Vue.extend({
template: '#country-template',
props: ['country', 'activated'],
data: () => ({ available: true })
});
const regionComponent = Vue.extend({
template: '#region-template',
props: ['region-name', 'region'],
data: function () {
const result = {
countriesActivated: {}
};
for (const c of Object.keys(this.region.countries)) {
result.countriesActivated[c] = { activated: true };
}
return result;
},
components: {
'country-c': countryComponent
},
computed: {
activated: {
get: function() {
let trueCount = 0;
let falseCount = 0;
for (const cName of Object.keys(this.countriesActivated)) {
if (this.countriesActivated[cName]) {
++trueCount;
} else {
++falseCount;
}
}
if (trueCount === 0) {
return false;
}
if (falseCount === 0) {
return true;
}
return 'mixed';
},
set: function(newValue) {
for (const cName of Object.keys(this.countriesActivated)) {
this.countriesActivated[cName] = newValue;
}
}
}
}
});
new Vue({
el: 'body',
data: {
regions: rawData
},
components: {
'region-c': regionComponent
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<template id="region-template">
<li>
<label>{{ regionName }}</label>
<input v-if="activated !== 'mixed'" type="checkbox" v-model="activated">
<span v-else>
</span>
<ul>
<country-c v-for="(countryName, country) in region.countries" :country="country" :activated.sync="countriesActivated[countryName]"></country-c>
</ul>
</li>
</template>
<template id="country-template">
<li>
<label for="country-{{ country.code }}">{{ country.name }}</label>
<input id="country-{{ country.code }}" type="checkbox" :disabled="!available" v-model="activated">
</li>
</template>
<ul>
<region-c v-for="(regionName, region) in regions" :region-name="regionName" :region="region" :countriesActivated=""></region-c>
</ul>
Source:stackexchange.com