daydreamer
daydreamer

Reputation: 91999

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.

Upvotes: 16

Views: 14098

Answers (9)

Aosi
Aosi

Reputation: 111

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.

Upvotes: -1

cowboy_programmer
cowboy_programmer

Reputation: 101

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>

Upvotes: 0

osm0sis
osm0sis

Reputation: 31

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

Upvotes: -1

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

Upvotes: 11

Shlomi Loubaton
Shlomi Loubaton

Reputation: 732

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.

Upvotes: 1

Lalit
Lalit

Reputation: 51

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

Upvotes: 5

dpineda
dpineda

Reputation: 2449

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>

Upvotes: 2

francisco.preller
francisco.preller

Reputation: 6639

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

Upvotes: 1

user2789093
user2789093

Reputation: 371

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.

Upvotes: 2

Related Questions