I made a small angular module to integrate with the bootstrap “buttons-group” (‘s plus a bit of javascript to make them work like radio buttons. I made it on this module to do the same for checkboxes: http://jsfiddle.net/evaneus/z9rge/
My code is at http://jsfiddle.net/askbjoernhansen/YjMMD/
My questions:
1) Is this the right approach?
2) Will the model be watched three times, or does $scope.$watch() figure out that it’s the same model and just do it once? It seems that way.
3) Is it “correct” to muck around with the DOM in the $watch function like I do? It feels “dirty”, but I guess that’s what I am asking for when I am adding angular to something that’s not angularjs compatible natively. Or?
4) Is there a way to put the ng-model attribute on the btn-group instead of on each button? That’d make it appear a lot cleaner.
You can see it at my jsfiddle above, or the code is here, first the html:
<!-- to test the two-way model -->
<input name="test" type="radio" ng-model="myModel['A']" value="left"> Left<br>
<input name="test" type="radio" ng-model="myModel['A']" value="middle"> Middle<br>
<input name="test" type="radio" ng-model="myModel['A']" value="right"> Right<br>
myModel A {{myModel['A']}}<br/>
<div class="btn-group" data-toggle="buttons-radio">
<button type="button" buttons-radio=""
ng-model="myModel['A']" value="left" class="btn">Left</button>
<button type="button" buttons-radio=""
ng-model="myModel['A']" value="middle" class="btn">Middle</button>
<button type="button" buttons-radio=""
ng-model="myModel['A']" value="right" class="btn">Right</button>
</div>
And the javascript:
angular.module('buttonsRadio', []).directive('buttonsRadio', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, element, attr, ctrl) {
element.bind('click', function() {
$scope.$apply(function(scope) {
ctrl.$setViewValue(attr.value);
});
});
// This should just be added once, but is added for each radio input now?
$scope.$watch(attr.ngModel, function(newValue, oldValue) {
element.parent(".btn-group").find('button').removeClass("active");
element.parent(".btn-group")
.find("button[value='" + newValue + "']").addClass('active');
});
}
};
});
You are invoking the directive three times, once for each button. AFAIK this means the linking function is called three times, binding events and watching for changes. added a little more on this below.
It’s fine to change the DOM when models change, but what you are doing – changing classes and values – can be done automatically by angular using bi-directional data-binding and native directives such as
ng-classYou can create a directive that renders buttons given a list of options. For example if you have this model and these options
You can create a
buttons-radiodirective like thiswhich can be invoked from your HTML
Things to notice:
ng-repeatdirective which goes through all options and compiles buttons accordingly.modelandoptionsto accept bi-directional binding, so angular does its magic when the directive changes their values.activeclass.activate(from the directives controller) is called, and it changes the value of the model.Heres a JSFiddle http://jsfiddle.net/jaimem/A5rvg/4/
EDIT
I wasn’t completely sure about this, but yes, your model will have three different watchers, one for every directive invocation. If you are using Batarang you can see all the watched expressions from the Performance tab and the AngularJS Properties panel. Batarang also exposes an
$scopeconvenience property, so you can look for your model’s watch objects by running the following from the console