使用AngularJS实现Bootstrap的“按钮-单选”切换

19

我创建了一个小的Angular模块,与Bootstrap中的"buttons-group"集成(加上一些javascript代码让它们像单选按钮一样工作)。我在这个模块的基础上创建了一个用于复选框的类似功能:http://jsfiddle.net/evaneus/z9rge/

我的代码在这里:http://jsfiddle.net/askbjoernhansen/YjMMD/

我的问题:

1)这是正确的方法吗?

2)模型会被监视三次,还是$scope.$watch()能够判断它是相同的模型并且只执行一次?看起来是这样的。

3)在$watch函数中混合DOM是否“正确”?感觉有点“不干净”,但是当我将Angular添加到原本不兼容AngularJS的东西时,这就是我要求的。或者说呢?

4)是否可以将ng-model属性放在btn-group上而不是每个按钮上?这将使它看起来更清洁。

您可以在我的jsfiddle上查看它,或者这里是代码,首先是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>

并且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');
            });
        }
    };
});​

我在想,你指令中链接函数的第四个参数(Ctrl)到底是什么? - Cybrix
@Cybrix 我认为那将是 Angular 控制器;但我已经很久没有使用 AngularJS 了,所以记不清了 :-) - Ask Bjørn Hansen
2个回答

26

你正在调用该指令三次,每个按钮都调用一次。据我所知,这意味着链接函数被调用了三次,绑定事件并监听更改。下面会对此进行详细解释。

当模型发生变化时,更改DOM是可以的,但是你所做的——更改类和值——可以通过双向数据绑定和内置指令(如ng-class)自动完成。

你可以创建一个指令,根据选项列表来渲染按钮。例如,如果你有这个模型和这些选项:

$scope.myOptions = ["Left", "Middle", "Right"];
$scope.myModel = "Left"

您可以像这样创建一个 buttons-radio 指令

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

可以从您的HTML中调用

<buttons-radio class="btn-group" data-toggle="buttons-radio" 
               model='myModel'    
               options='myOptions'>
</buttons-radio>

需要注意的几点:

  1. 模板使用了一个 ng-repeat 指令,用于循环所有选项并相应地编译按钮。
  2. 该指令具有自己的控制器和隔离作用域,其中包括 modeloptions 以接受双向绑定,因此当指令更改其值时,angular 将执行其魔法。
  3. 如果选项与模型的当前值匹配,则给按钮分配一个 active 类。
  4. 当按下按钮时,调用指令控制器中的 activate 方法,并更改模型的值。

这是一个 JSFiddle 示例:http://jsfiddle.net/jaimem/A5rvg/4/


编辑

我不是完全确定,但是是的,您的模型将有三个不同的监视器,每个指令调用都会有一个。如果您使用 Batarang,您可以在 性能 标签和 AngularJS 属性 面板中查看所有被监视的表达式。Batarang 还提供了一个 $scope 方便属性,因此您可以通过从控制台运行以下命令来查找模型的监视对象:

$scope.$$watchers.filter(function(w){return w.exp ==="myModel['A']"}); 

太好了,感谢jm!它可以直接使用,而且非常容易进行微调。还要感谢Batarang的参考,我以前没有看过。我的旧版本在实际应用中不能正确地执行双向操作,而你的解决方案可以。太棒了,再次感谢!我保留了angular.module()语法;这似乎更好,因为我可以将其放入可重用模块中,对吗? - Ask Bjørn Hansen
不用谢,是的,将相关指令放入模块中是有意义的。 - jaime
对于任何困扰于缩小代码的人来说,控制器构造函数可以使用注释风格重写: controller: ['$scope', function ($scope) { $scope.activate = function (option) { $scope.model = option; };}] - roufamatic
2
工作得很好,谢谢!然而,在使用Twitter Bootstrap 3时,“active”类没有被设置。有什么见解吗?已更新fiddle:http://jsfiddle.net/A5rvg/79/ - Victor

7

这很简单

<html>
    <div class="btn-group" data-toggle="buttons">
      <span class="btn btn-primary" ng-click="worktype=1" ng-class="worktype==1?'active':''">
        <input type="radio"   value="1">全职
      </span>
      <span class="btn btn-primary" ng-click="worktype=2" ng-class="worktype==2?'active':''">
        <input type="radio"   value="2">兼职
      </span>
    </div>
</html>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接