AngularJS: Radio buttons do not work with Bootstrap 3

AngularJS: Radio buttons do not work with Bootstrap 3

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.

Solutions/Answers:

Solution 1:

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.

Solution 2:

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

Related:  Font awesome not working in Firefox

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;
}

Solution 3:

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))
})

Solution 4:

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>
Related:  How to make bootstrap carousel image responsive?

Solution 5:

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;
          });
        }
      });
    }
  };
})

Solution 6:

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.

Related:  Pulling edited text from summernote textarea

References