vuejs3v-model

VueJS3 : radio v-model doesn't update when switch changed


I want my input radio that is default checked to update the value in the v-model. But when I switch plan the v-model doesn't update dynamically. I'm stuck. Maybe I have to take a look at "watch" or "ref". My formValues is reactive and returns the good answer in the v-model. But when I switch plan... doesn't update the check output.

Demo

<template>
    <!-- Second step -->
    <section id="second-step">

        <div class="row mt-4 px-2">
            <div class="col-12">
                <h1 class="h2 fw-bold">Select your plan</h1>
                <p class="pt-1">
                    You have the option of monthly or yearly billing.
                </p>
            </div>
        </div>

        <!-- Form 2 -->
        <form class="row justify-content-xl-between px-3 pe-xl-0" id="form-plan">

            <!-- Plan 1 -->
            <div class="col-12 col-xl plan mt-3 p-0">
                <label for="arcade-plan" class="plan-label"></label>
                <input v-model="check" type="radio" v-if="formValues.billing.monthly" :value="formValues.plan[0].name" name="plan" id="arcade-plan" checked>
                <input v-model="check" type="radio" v-else="formValues.billing.yearly" :value="formValues.plan[1].name" name="plan" id="arcade-plan" checked>
                <div class="plan-content card ps-2">
                    <div class="plan-icon mt-1">
                        <img src="assets/images/icon-arcade.svg" alt="">
                    </div>
                    <div class="plan-description">
                        <span class="plan-title mb-0">Arcade</span>
                        <div class="price monthly">
                            <div class="amount mb-0 fs-6 fw-medium text-cool-gray">$9/mo</div>
                        </div>
                        <div class="price yearly d-none">
                            <div class="amount fs-6 fw-medium text-cool-gray">$90/yr</div>
                            <div class="billing-msg text-primary">2 months free</div>
                        </div>
                    </div>
                </div>
            </div>


            <!-- Plan 2 -->
            <div class="col-12 col-xl plan mt-3 p-0">
                <label for="advanced-plan" class="plan-label"></label>
                <input v-model="check" type="radio" v-if="formValues.billing.monthly" :value="formValues.plan[2].name" name="plan" id="advanced-plan">
                <input v-model="check" type="radio" v-else="formValues.billing.yearly" :value="formValues.plan[3].name" name="plan" id="advanced-plan">
                <div class="plan-content card ps-2">
                    <div class="plan-icon mt-1">
                        <img src="assets/images/icon-advanced.svg" alt="">
                    </div>
                    <div class="plan-description">
                        <span class="plan-title mb-0">Advanced</span>
                        <div class="price monthly">
                            <div class="amount mb-0 fs-6 fw-medium text-cool-gray">$12/mo</div>
                        </div>
                        <div class="price yearly d-none">
                            <div class="amount fs-6 fw-medium text-cool-gray">$120/yr</div>
                            <div class="billing-msg text-primary">2 months free</div>
                        </div>
                    </div>
                </div>

            </div>

            <!-- Plan 3 -->
            <div class="col-12 col-xl plan mt-3 p-0">
                <label for="pro-plan" class="plan-label"></label>
                <input v-model="check" type="radio" v-if="formValues.billing.monthly" :value="formValues.plan[4].name" name="plan" id="pro-plan">
                <input v-model="check" type="radio" v-else="formValues.billing.yearly" :value="formValues.plan[5].name" name="plan" id="pro-plan">
                <div class="plan-content card ps-2">
                    <div class="plan-icon mt-1">
                        <img src="assets/images/icon-pro.svg" alt="">
                    </div>
                    <div class="plan-description">
                        <span class="plan-title mb-0">Pro</span>
                        <div class="price monthly">
                            <div class="amount mb-0 fs-6 fw-medium text-cool-gray">$15/mo</div>
                        </div>
                        <div class="price yearly d-none">
                            <div class="amount fs-6 fw-medium text-cool-gray">$150/yr</div>
                            <div class="billing-msg text-primary">2 months free</div>
                        </div>
                    </div>
                </div>

            </div>



            <!-- Switch billing -->
            <div class="col-12 py-3 rounded">
                <div class="row bg-alabaster justify-content-center px-2">
                    <div class="col-10 col-sm-8 col-md-6">
                        <div class="switch-wrapper py-2 mb-0">
                            <input id="monthly" type="radio" name="switch" checked>
                            <input id="yearly" type="radio" name="switch">
                            <label id="billing-monthly" for="monthly" class="mx-sm-5">Monthly</label>
                            <label id="billing-yearly" for="yearly" class="mx-sm-5">Yearly</label>
                            <span class="toggler"></span>
                        </div>
                    </div>
                </div>
            </div>
        </form>

    </section>
    <div class="container">
        Selected :
        {{ check }}
    </div>
</template>

<script>
import { onMounted } from 'vue';
export default {
    data() {
        return {
            check: 'Arcade (Monthly)'
        }
    },
    props: {
        step: Number,
        formValues: Object
    },
    setup(props) {
        onMounted(() => {
            const switchInputs = document.querySelectorAll(".switch-wrapper input");
            const prices = document.querySelectorAll(".price");
            const toggleClass = "d-none";
            /* Switch */
            for (const switchInput of switchInputs) {
                switchInput.addEventListener("input", function () {
                    for (const price of prices) {
                        price.classList.add(toggleClass);
                    }
                    const activePrices = document.querySelectorAll(
                        `.price.${switchInput.id}`
                    );
                    for (const activePrice of activePrices) {
                        activePrice.classList.remove(toggleClass);
                    }

                    /* Change billing type */
                    props.formValues.updateBilling()
                    console.log(props.formValues.billing.yearly)

                })
            }
        })
}
}





</script>

I didn't find any sources about this problem yet. I think I can do something with a watch or ref.


Solution

  • Here is a very basic sample of Radio input binding with Vue:

    const { createApp } = Vue;
        
    const App = {
      data() {
          return {
              check: 'Arcade (Monthly)',
              items: ['Arcade (Monthly)', 'Advanced (Monthly)']
          }
      }
    }
    
    const app = createApp(App)
    app.mount('#app')
    <div id="app">  
      <div>Picked: {{ check }}</div><br />  
      <div v-for="(item, index) in items">
        <input type="radio" :id="`item_${index}`" :value="item" v-model="check" />
        <label :for="`item_${index}`">{{item}}</label>
      </div>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>