AngularJS: Radio buttons do not work with Bootstrap 3
Asked Answered
C

9

16

I have a radio button, which sets the value of True or False based on the value of transaction type

The demo can be found here

The problem is when I click on any of the radio button, the value of $scope.transaction.debit does not change

My javascript code is

    var app = angular.module('myApp', []);

    app.controller("MainCtrl", function($scope){
      $scope.transaction = {};
      $scope.transaction.debit=undefined;

      console.log('controller initialized');
    });

Please let me know what I am doing wrong.

Also, I do not want to use Angular-UI or AngularStrap for this purpose, unless no other option is available.

Chaconne answered 3/10, 2013 at 4:47 Comment(2)
You are right. Removing the bootstrap code around your radio buttons makes them work again. I suspect the radio buttons are actually not being selected due to bootstrap? Note that removing the script reference to the bootstrap.js file also makes them work again, even with the working cssLavonnelaw
You may also look for similar problem (Angular-Strap Radio Button won't update model). Althought it concerns a collision of libraries and it mainly refers to bootstrap3 + angular-strap, but someone (or you in future) may look for it.Wille
T
2

You have a large label stuck over the top of the radio buttons which prevents input to your radio buttons. The html should read:

 <input type="radio" data-ng-model="transaction.debit" value="True">Debit</input>

 <input type="radio" data-ng-model="transaction.debit" value="False">Credit</input>

It then works, of course it may not look the way you want it to then.

Theressathereto answered 3/10, 2013 at 5:9 Comment(4)
Removing the bootstrap.js file fixes the issue, which leads me to believe it has nothing to do with the labels, but more to do with the javascript bootstrap is injecting. Meanwhile, removing the labels of course ceases the bootstrap javascript from running on those objects.Lavonnelaw
An alternative is to handle the click and the data-interaction using ng-click on the labels. You would then not have to remove bootstrap javascript. plnkr.co/edit/8i86U2?p=previewTheressathereto
This link explains how to use Bootstrap and Angular: scotch.io/tutorials/…Opossum
Even Plunker is linting about this wrong </input> closing tag. How's that a chosen answer?Eversole
T
11

I modified dpineda's solution. You can use without removing bootsrap.js dependency. Also there is a working example here.

This is the flow:

  1. Remove data-toggle="buttons" for preventing bootstrap execution.
  2. Add some CSS for fixing the broken view (btn-radio css class)
  3. Add some AngularJS logic for checked style effect.

html

<div class="btn-group col-lg-3">
  <label class="btn btn-default btn-radio" ng-class="{'active': transaction.debit == '0'}">
    <input type="radio" data-ng-model="transaction.debit" value="0"> Debit
  </label>
  <label class="btn btn-default btn-radio" ng-class="{'active': transaction.debit == '1'}">
    <input type="radio" data-ng-model="transaction.debit" value="1"> Credit
  </label>
</div>

<p>Transaction type: {{transaction.debit}}</p>

JavaScript

var app = angular.module('myApp', []);

app.controller("MainCtrl", function($scope) {

  $scope.transaction = {
    debit: 0
  };
});

Style

.btn-radio > input[type=radio] {
  position       : absolute;
  clip           : rect(0, 0, 0, 0);
  pointer-events : none;
}
Tersanctus answered 28/3, 2016 at 21:16 Comment(2)
I found the key issue was remove the data-toggle="buttons" to disable that bit of bootstrap.js functionality and then add the above style to compensate for the reappearance of the actual radio buttons.Carmoncarmona
I still faced this issue in Angular 6 which I resolved by removing data-toggle="buttons" and adding [class.active]="option == 'value'" to each label. I did not need to make any CSS changes.Leolaleoline
T
5

I found the problem in bootstrap.js. Comment the line e.preventDefault(), it works.

// BUTTON DATA-API
// ===============
$(document)
.on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
    var $btn = $(e.target)
    if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
    Plugin.call($btn, 'toggle')
    e.preventDefault() //Before
    //e.preventDefault() //After
})
.on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
    $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
})
Tracheo answered 28/11, 2014 at 19:40 Comment(0)
T
2

You have a large label stuck over the top of the radio buttons which prevents input to your radio buttons. The html should read:

 <input type="radio" data-ng-model="transaction.debit" value="True">Debit</input>

 <input type="radio" data-ng-model="transaction.debit" value="False">Credit</input>

It then works, of course it may not look the way you want it to then.

Theressathereto answered 3/10, 2013 at 5:9 Comment(4)
Removing the bootstrap.js file fixes the issue, which leads me to believe it has nothing to do with the labels, but more to do with the javascript bootstrap is injecting. Meanwhile, removing the labels of course ceases the bootstrap javascript from running on those objects.Lavonnelaw
An alternative is to handle the click and the data-interaction using ng-click on the labels. You would then not have to remove bootstrap javascript. plnkr.co/edit/8i86U2?p=previewTheressathereto
This link explains how to use Bootstrap and Angular: scotch.io/tutorials/…Opossum
Even Plunker is linting about this wrong </input> closing tag. How's that a chosen answer?Eversole
H
2

if you remove de bootstrap code you can control the styles with conditionals

<label class="btn btn-default" ng-class="{'active': transaction.debit == 'some'}">
    <input type="radio" data-ng-model="transaction.debit" name="debit" value="some"> Some
</label>
<label class="btn btn-default" ng-class="{'active': transaction.debit == 'other'}">
    <input type="radio" data-ng-model="transaction.debit" name="debit" value="other"> Other
</label>
Howerton answered 11/9, 2014 at 17:54 Comment(0)
L
1

Here's a working version using a new directive:

html

<section ng-controller="MainCtrl">
<div class="form-group">
    <label class="col-lg-2 control-label">Type</label>

    <div class="btn-group col-lg-3" data-toggle="buttons">
        <label class="btn btn-default" radio-button ng-model="transaction.debit" value="True">
            Debit
        </label>
        <label class="btn btn-default" radio-button ng-model="transaction.debit" value="False">
            Credit
        </label>
    </div>

    <p>Transaction type: {{transaction.debit}}</p>
</div>
</section>

javascript

// Code goes here
var app = angular.module('myApp', []);

app.controller("MainCtrl", function($scope){
  $scope.transaction = {};
  $scope.transaction.debit=undefined;

  console.log('controller initialized');
});

app.directive("radioButton", function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, element, attrs, ctrl) {
      element.bind('click', function () {
        if (!element.hasClass('active')) {
          scope.$apply(function () {
            scope.transaction.debit = attrs.value;
          });
        }
      });
    }
  };
})
Lavonnelaw answered 3/10, 2013 at 5:44 Comment(1)
The directive is way too specific. Losing bootstrap functionality with your solution. .debit=undefined is doing nothing at all. -1Evapotranspiration
F
1

Based on francisco.preller's answer I wrote two solutions trying to make it fit for generic use, without loosing the input tags: html:

        <label class="btn btn-info" radiobuttonlbl>
          <input ng-model="query.gender" type="radio" value="0">male
        </label>

solution #1:

.directive("radiobuttonlbl", function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs, ctrl) {
      element.bind('click', function () {
        var input_elem = angular.element(element.find('input')[0]);
        (function(o, s, v) {
          s = s.replace(/\[(\w+)\]/g, '.$1');
          s = s.replace(/^\./, '');
          var a = s.split('.').reverse();
          while(a.length>1) {
            var k = a.pop();
            o = o[k];
          }
          scope.$apply(function(){ o[a.pop()]=v;});
        })(scope, input_elem.attr('ng-model'), input_elem.attr('value'));
      });
    }
  };
})

Solution #2:

.directive("radiobuttonlbl", function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs, ctrl) {
      element.bind('click', function () {
        var input_elem = angular.element(element.find('input')[0]);
        input_elem.prop('checked',true);
        input_elem.triggerHandler('click'); 
      });
    }
  };
})

I have a feeling the first one is better because it make angular do the updating work.

Fluff answered 10/3, 2016 at 22:27 Comment(1)
This answer is not ideal but the best one on this page.Evapotranspiration
T
0

If someone is still searching for an easy way to do this (I personally am hesitant to overload my code with directives), here is what I did:

You can set the value using ng-click on the label. Furthermore, notice the ng-init and active class on the label of the first radio item. This way, you can let bootstrap do its thing, and angular do its thing. The only drawback is you are not letting angular control this using ng-model.

<div class="btn-group col-lg-3" data-toggle="buttons">
      <label class="btn btn-default active" ng-init="transaction.debit=true" ng-click="transaction.debit=true">
        <input type="radio" checked> Debit
      </label>
      <label class="btn btn-default" ng-click="transaction.debit=false">
        <input type="radio"> Credit
      </label>
    </div>
Twocolor answered 1/12, 2019 at 22:52 Comment(0)
M
-1

I have the same problem, in my case, the default style change and can't use angular ng-model inside any radio or checkbox button. So i read some articles and found that sometimes if you load JQuery after Bootstrap it overwrites any other instance of jQuery, and it prevent default styles and components to be loaded as bootstrap components, this also happens if you load angularJS after jQuery or viceversa.

PS.- My answer: Check your load script stack, play with it and find which order works for you. (first jquery, then angularJs, finally bootstrap). Usually you require to jQuery to be the first option, Angular and almost every new framework works on top of it. Cheers.

Methodist answered 7/4, 2015 at 19:6 Comment(2)
Perfect. This was the issue. I loaded my bootstrap.min.js before jquery.min.js and the problem was totally vanished ! Thanks.Occasion
Unfortnatelly, this resolved this specific problem but broke everything else since jQuery is a dependency for BootstrapOccasion
S
-1

I had the same problem. Use ng-click on your labels and it will work fine with bootstrap

<label class="btn btn-default" ng-click="transaction.debit = 'debit'">

Here it's working in plunker

Shirley answered 4/8, 2017 at 9:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.