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-class
You can create a directive that renders buttons given a list of options. For example if you have this model and these options
$scope.myOptions = ["Left", "Middle", "Right"];
$scope.myModel = "Left"
You can create a buttons-radio
directive like this
App.directive('buttonsRadio', function() {
return {
restrict: 'E',
scope: { model: '=', options:'='},
controller: function($scope){
$scope.activate = function(option){
$scope.model = option;
};
},
template: "<button type='button' class='btn' "+
"ng-class='{active: option == model}'"+
"ng-repeat='option in options' "+
"ng-click='activate(option)'>{{option}} "+
"</button>"
};
});
which can be invoked from your HTML
<buttons-radio class="btn-group" data-toggle="buttons-radio"
model='myModel'
options='myOptions'>
</buttons-radio>
Things to notice:
ng-repeat
directive which goes through all options and compiles buttons accordingly.model
and options
to accept bi-directional binding, so angular does its magic when the directive changes their values.active
class.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 $scope
convenience property, so you can look for your model's watch objects by running the following from the console
$scope.$$watchers.filter(function(w){return w.exp ==="myModel['A']"});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With