5

I have the following code:

<template>
    <div class="btn-group" data-toggle="buttons">
        <label class="btn btn-outline-dark active">
            <input type="radio" name="grp" v-model="channel" value="vh1" checked> VH1
        </label>
        <label class="btn btn-outline-dark">
            <input type="radio" name="grp" v-model="channel" value="mtv"> MTV
        </label>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                channel: 'mtv'
            }
        },
        watch: {
            channel: function(newValue) {
                console.log('value has been changed to ' + newValue);
            }
        }
     }
</script>

When I click on a radio button, nothing happens. But when I removed the "data-toggle="buttons" attribute for the styling, then it starts working! Can anyone help me find a work around here?

Edit

For those of you who don't know, data-toggle is from bootstrap button plugin, it adds extra styles and functionality to the buttons you are making. Check out the docs here: https://getbootstrap.com/docs/4.0/components/buttons/#toggle-states

sksallaj
  • 3,872
  • 3
  • 37
  • 58

2 Answers2

12

Remove data-toggle and set active class by :class="{ 'active': channel === [value] }" for each input.

pandora2000
  • 463
  • 1
  • 4
  • 12
  • 1
    Thank you, this worked perfect for me. This should be the accepted answer. – Niicodemus Aug 12 '18 at 14:01
  • 1
    Thanks! This is simpler and I like it, less moving parts. Because I added a dark theme to it, I did this: :class="{'active': channel === [value], 'btn' : 'btn', 'btn-outline-dark' : 'btn-outline-dark'}" – sksallaj Aug 13 '18 at 21:05
  • I'm having this same issue and trying to implement the solution. However I don't understand where you're getting the `[value]` from? Is that referencing the value of the radio button? Where is that value in brackets coming from? Thanks – ItsPronounced Sep 12 '18 at 15:29
  • @drpcken [value] is what you set at value attribute of input tag. – pandora2000 Sep 13 '18 at 18:59
  • That's what I assumed, however you need the `[value]` of the radio button for that to work, right? The `active` class has to be set to the label of the radio button, not the actual radio class (right?). I solved it by putting the value of the radios I'm using into data object arrays and iterating through them using `v-for` in my labels and setting the `active` class like above. – ItsPronounced Sep 13 '18 at 20:02
2

Finally got the solution

After long hours of searching, I was able to come up with a way to hack into the buttons.

Step 1

Change the above template to the following (removed v-model from the inputs)

<template>
    <div class="btn-group" data-toggle="buttons" v-radio="channel">
        <label class="btn btn-outline-dark active">
            <input type="radio" name="grp" value="vh1" checked> VH1
        </label>
        <label class="btn btn-outline-dark">
            <input type="radio" name="grp" value="mtv"> MTV
        </label>
    </div>
</template>

This solution was pretty difficult to find, and the solution was pretty hacky, but it got the job done.

Step 2 (UPDATED 6/18/2018)

Create a directive

In my case, since I was using single file components, I needed to bring it directives in the following way:

radio.js

export default {
    inserted: function (el, binding) {
        var btns = $(el).find('.btn');
        var radioGrpName = $(btns[0]).find('input')[0].name;
        $("input[name='" + radioGrpName + "'][value='" + binding.value + "']").closest('.btn').button('toggle');
    },
    bind: function (el, binding, vnode) {
        var btns = $(el).find('.btn');
        btns.each(function () {
            $(this).on('click', function () {
                var v = $(this).find('input').get(0).value;
                (function set(obj, str, val) {
                    str = str.split('.');
                    while (str.length > 1) {
                        obj = obj[str.shift()];
                    }
                    return obj[str.shift()] = val;
                })(vnode.context, binding.expression, v);
            })
        })
    }
}

What this does is it binds a jquery click event to each radio button found in the div containing the v-radio. The second part where I'm doing function set (copied from the answer in reference), it checks to see if there's any dots in the expression, otherwise it sets the value of vnode.context["channel"], which updates the model. The bind hooks up to events before the component is loaded so it can be ready to fire off the internal workings of the radio button.

The inserted function is called after bind, during the physical insertion of the component into the parent node. So when you set an initial state (no event fired), the component will automatically reflect the value set in data.

Step 3

add directives radio to the script

import radio from '../../directives/radio'
<script>
    export default {
        directives: {
            radio: radio  
        },
        data() {
            return {
                channel: 'mtv'
            }
        },
        //you can use computed property, but you need get and set
        watch: {
             channel: function(newValue) {
                 console.log('value has been changed to ' + newValue);
             }
         }
     }
 </script>

Using these links as references:

sksallaj
  • 3,872
  • 3
  • 37
  • 58
  • 1
    Nice solution. But I think there is missing secondary argument "binding" in line "inserted: function (el) {" – BitLord Nov 01 '19 at 05:48
  • Hi BitLord, thanks for the suggestion. It's been a while since I did this, but I'll take your word for it. Updated the answer. – sksallaj Nov 07 '19 at 18:41