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.

steampowered
  • 11,809
  • 12
  • 78
  • 98
daydreamer
  • 87,243
  • 191
  • 450
  • 722
  • 1
    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 css – francisco.preller Oct 03 '13 at 05:06
  • You may also look for similar problem ([Angular-Strap Radio Button won't update model](http://stackoverflow.com/q/23613450/1977012)). 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. – Egel Jan 22 '15 at 10:47

9 Answers9

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;
}
Community
  • 1
  • 1
Fırat Küçük
  • 5,613
  • 2
  • 50
  • 53
  • 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. – Lee H Jul 04 '16 at 02:49
  • 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. – Burhan Jan 18 '19 at 04:43
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))
})
Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
Lalit
  • 51
  • 1
  • 2
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.

user2789093
  • 371
  • 3
  • 12
  • 3
    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. – francisco.preller Oct 03 '13 at 05:12
  • 6
    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. http://plnkr.co/edit/8i86U2?p=preview – user2789093 Oct 03 '13 at 05:45
  • This link explains how to use Bootstrap and Angular: https://scotch.io/tutorials/how-to-correctly-use-bootstrapjs-and-angularjs-together – Chocksmith Apr 08 '16 at 19:57
  • Even Plunker is linting about this wrong `` closing tag. How's that a chosen answer? – Vladius Apr 21 '16 at 12:49
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>
dpineda
  • 2,401
  • 27
  • 25
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;
          });
        }
      });
    }
  };
})
francisco.preller
  • 6,559
  • 4
  • 28
  • 39
  • The directive is way too specific. Losing bootstrap functionality with your solution. `.debit=undefined` is doing nothing at all. -1 – A1rPun Aug 31 '17 at 15:06
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.

Shlomi Loubaton
  • 733
  • 6
  • 9
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>
-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.

Aosi
  • 111
  • 2
  • 7
  • 1
    Perfect. This was the issue. I loaded my bootstrap.min.js before jquery.min.js and the problem was totally vanished ! Thanks. – Nabeel Mar 20 '17 at 16:37
  • 2
    Unfortnatelly, this resolved this specific problem but broke everything else since jQuery is a dependency for Bootstrap – Nabeel Mar 20 '17 at 16:41
-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

osm0sis
  • 31
  • 3