feat: implement true-value and false-value to ASwitch and ACheckbox
sandros94 opened this issue · 8 comments
Following Vue's documentation:
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" />true-value and false-value are Vue-specific attributes that only work with v-model. Here the toggle property's value will be set to 'yes' when the box is checked, and set to 'no' when unchecked. You can also bind them to dynamic values using v-bind:
<input type="checkbox" v-model="toggle" :true-value="dynamicTrueValue" :false-value="dynamicFalseValue" />
There is a native Vue feature to assign specific strings to a checkbox if it is true or false.
This is especially useful to reduce the necessary code needed to work with tools such as Color-Mode. Where you need to assign a specific string to colorMode.preference
to select the desired theme.
We could have this:
<ASwitch v-model="colorMode.preference" true-value="dark" false-value="light" />
Instead of this:
<template>
<div>
<ASwitch v-model="themeToggle" />
</div>
</template>
<script setup>
const colorMode = useColorMode()
const themeToggle = ref(false)
watch(themeToggle, (newValue) => {
colorMode.preference = newValue ? 'dark' : 'light'
})
</script>
Not to mention that reading the actual value will get the switch to it's correct position. (excluding the 'system' preference)
I'm still quite new to javascript, so I'm not confident enough to open a PR without knowing exactly what files I need to change.
For a small background on how I got to this issue you can read this discussion.
I've forgot to point out this issue too:
In short when the v-model changes quickly right after the page load the ASwitch doesn't change it's visual state, but the v-model variable is in the correct state.
Ex, for:
const themeToggle = ref(colorMode.value === 'dark');
at page load is false because for a short period of time colorMode.value = 'system'
but a few milliseconds later it updates to 'dark'
. This makes the themeToggle
go from false to true, but the actual visuals of the toggle don't change.
So I wanted to test stuff:
<template>
<div class="my-auto">
<label>
<input type="checkbox" :value="trueValue" v-model="isChecked" @change="onChange" />
{{ isChecked }}
</label>
</div>
</template>
<script setup>
const props = defineProps({
trueValue: {
type: [String, Number, Boolean],
default: true,
},
falseValue: {
type: [String, Number, Boolean],
default: false,
},
})
const emit = defineEmits(['update:modelValue'])
const onChange = (event) => {
emit('update:modelValue', event.target.checked ? props.trueValue : props.falseValue)
}
const isChecked = ref(false)
</script>
While defining trueValue
and falseValue
inside this component was the only thing need other than the @change
, atm I have no clue on how tick/untick when the value changes from outside.
Sorry, I didn't noticed checkbox is also there
@sandros94 We have value
prop for a checkbox that will act as true value. Can you please confirm?
@jd-solanki Yes I've read the pr about it with the comments, and looking at the code (for that little I know about js/ts) it looks good.
This might sound dumb, because I really feel like that and I'm new to this. But, could you point me to a doc (or shortly explain it to me) on how can I test new/updated components like this pr, before they get to a full package update? Do I only copy the new component's code and put in my nuxt test-env or should I copy the whole repo and place it somewhere (like inside the node_module folder)?
Hi @sandros94
First of all let's be clear on your feature requests:
- true false value for switch via mentioned PR and check out the demo here
- You can change checkbox value when checked via
value
prop in show in this demo - Your this comment
Regarding, last point in above three, two are done and one still to resolve, right?
For your third point, You need to use computed property instead of ref
. When you use const val = ref(colorMode.value === 'dark')
only the initial value will be set. This means on the first execution it will set const val = ref(false)
and that's it, value won't change until you update it via val.value = true
. Hence, in your case you need computed property that calculates based on another thing:
- const val = ref(colorMode.value === 'dark')
+ const val = computed(() => colorMode.value === 'dark')
Now, with this change val
will auto update when colorMode
changes.
Finally, You won't able to try mentioned PR with your existing anu installation. I hope you are familiar with updating packages. To try latest commits before release, please perform the changes explained in this guide.
I hope this will resolve your query.
My mistake, I didn't understood the question.
true false value for switch via mentioned PR and check out the demo here
Yes we are clear on that (later I'll use the edge package to test it, and yes I'm familiar with updating/changing packages, ty).
You can change checkbox value when checked via value prop in show in this demo
I did forget about its existence. But wouldn't it work only for the value being checked?
What if I want to set another value (or trigger something else) when I'm unchecking it? In a similar way as we are doing for the switch. If I'm not mistaken we could update the already present value
prop to be the new on-value
and we just need to add the script for the off-value
right? This way it will be easier to remember too, since two similar components are structured in a similar way.
You need to use computed property instead of ref.
Thanks for letting me discover the computed property, but I've spent the last couple of hour or so testing it and I can confidently say that ref wasn't the problem:
Up until the page is fully read and loaded by the browser the colorMode.value
is set to system
to not create problems while Nuxt is in SSR.
Using something like const val = ref(colorMode.value === 'dark')
I'm already defining a reactant variable that changes based on colorMode.value
. In fact, as soon as the browser ended loading the page (and the value goes from system
to light
xor dark
) I can see that val
becomes true
or false
based on dark
or light
, and it changes accordingly.
What is not changing tho is the ASwitch's visuals right after the first val
update at the end of page load. But if I use another component to change colorMode.preference
, thus updating the colorMode.value
, I see that ASwitch updates correspondingly.
Examples:
Here you can see that right after the page load colorMode.value
updated from system to dark (since the browser is in dark mode) and, accordingly, val
got updated to true
even if using ref to define it. But as you can see the ASwitch didn't get updated.
But if I use the radio buttons above to update the colorMode.preference
then the ASwitch gets updated correctly.
Here's the code:
<template>
<div class="w-full h-[100svh] flex flex-col items-center justify-center text-center">
<div class="flex flex-wrap gap-4 m-6">
<ARadio v-model="colorMode.preference" value="system" label="System" />
<ARadio v-model="colorMode.preference" value="light" label="Light" />
<ARadio v-model="colorMode.preference" value="dark" label="Dark" />
</div>
<div class="m-6 justify-center">
<div class="flex justify-center mb-2">
<ASwitch v-model="val" />
</div>
<p class="text-warm-gray-500">val: <span class="color-info">{{ val }}</span> {{ typeof val }}</p>
</div>
<div class="m-6 text-warm-gray-500">
<p>colorMode.preference: <span class="color-info">{{ colorMode.preference }}</span> {{ typeof colorMode.preference }}</p>
<p>colorMode.value: <span class="color-info">{{ colorMode.value }}</span> {{ typeof colorMode.value }}</p>
</div>
</div>
</template>
<style>
/* Light mode */
body {
background: #faf9f8;
}
/* Dark mode */
.dark body {
background: #0f0f0f;
}
</style>
<script setup>
const colorMode = useColorMode();
const val = ref(colorMode.value === 'dark');
watch(val, (newValue) => {
colorMode.preference = newValue ? 'dark' : 'light'
})
</script>
For more info you can checkout the test-repo I'm working on
TL;DR: I'm closing this issue because the last point that remained uncovered I'm mostly sure that is not related to the ASwitch.
Today I've got some free time and went back on this.
First off I wanted to make sure that I've understood correctly how the computed
property worked. Only that, this time I actually understood what get
and set
functions are and how to use them. Then I made the switch to be able to be set back to the current theme configured by the system.
Here is the final code:
<template>
<ClientOnly>
<template #fallback>
<ASwitch v-model="off" off-icon="i-svg-spinners:clock"/>
</template>
<ASwitch v-model="switchTheme" off-icon="i-ph:sun" on-icon="i-ph:moon"/>
</ClientOnly>
</template>
<script setup>
const colorMode = useColorMode();
const off = ref(false)
const switchTheme = computed({
get: () => colorMode.value === 'dark',
set: (value) => {
if (colorMode.preference === 'system') {
// Set the opposite of the current system value
colorMode.preference = value ? 'dark' : 'light';
} else {
// Set the preference back to 'system'
colorMode.preference = 'system';
}
}
});
</script>
Now, about that <ClientOnly>
:
I'm not sure why I didn't think of this earlier, but since I only need to know the system's current theme after the page has fully loaded by the browser, I can simply wait for it using the built-in component provided by Nuxt.
Then I added a dummy ASwitch just to animate it with a spinning clock until the actual one was ready.