Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VUE 2 with Bootstrap 4 - Radio button action not working when using data-toggle="buttons"

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

like image 693
sksallaj Avatar asked Feb 28 '18 07:02

sksallaj


2 Answers

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

like image 161
pandora2000 Avatar answered Sep 20 '22 15:09

pandora2000


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:

  • Old custom directive hack using vue 1
  • Update model from custom directive
like image 25
sksallaj Avatar answered Sep 19 '22 15:09

sksallaj