caoruiy‘s blog

Wisdom outweighs any wealth

[ui-bootstrap源码解析系列04]-button模块

概述

button 模块是用来使用按钮模拟 checkbox 或者 radio 的模块,其主要功能也在于此,你可以在https://github.com/caoruiy/bootstrap/tree/master/src/buttons查看到模块源码,源码中添加了许多中文注释,方便阅读。

用法

通过 buttons 指令,我们可以使按钮组的行为类似于chexkbox(uib-btn-checkbox) 或者 radio(uib-btn-radio)

uib-btn-checkbox 设置项

  • btn-checkbox-false (Default: false) – 设置按钮为未选中状态时 ng-model 的值。

  • btn-checkbox-true (Default: true) – 设置按钮为选中状态时 ng-model 的值。

  • ng-model $ – 设置 checkbox 的。 默认情况下是 true 或者 false,当设置了 btn-checkbox-false 或者 btn-checkbox-true 时,以设置的值为准。

uib-btn-radio 设置项

  • ng-model $ – 设置 radio 状态. radio 按钮组需要设置相同的 ng-model

  • uib-btn-radio$ radio 按钮的值,当选中该按钮时,会把该值传递给 ng-model。

  • uib-uncheckable $ (Default: null) – 接受一个表达式,如果该表达式的值是 true 的,说明该按钮可取消选中状态,否则可以。为 true 时相当于在按钮上添加了 uncheckable 属性一样,只不过该属性可以动态控制。

  • uncheckable B – 是否一个 radio 按钮可以被取消选中状态,添加该属性,则可以被取消选中状态。

额外的设置项 uibButtonConfig

  • activeClass (Default: active) – 选中是添加的class类名。

  • toggleEvent (Default: click) – 按钮 toggle 事件,默认是 click 你可以修改成其他的,比如 mouseover

额外的设置项的使用可以在 控制器中引入该服务 uibButtonConfig ,在控制器中设置:

uibButtonConfig.activeClass = 'activeClassName';// 修改选中时添加的 class 名称
uibButtonConfig.toggleEvent = 'mouseover';// 修改触发事件,改成mouseover

已知问题

要在 btn-group 中的元素上使用 tooltips 或者 popovers,请将 tooltip / popover appendToBody选项设置为true。这是由于Bootstrap CSS样式。参见这里了解更多信息。

原理解析

由于 checkbox 和 radio 实现的原理相似,我们这里只说下 radio。

首先指令的实现依赖 自身ngModel
控制器本身是给指令模板使用的,但是被 require 的指令的控制器可以被注入到当前指令的 link 函数中,所以此处的依赖只是为了使用控制器内的方法。

首先 自身 控制器实现了 激活的事件和激活时添加的class类

ngModel 控制器顾名思义就是指令 ngModel 的控制器,这是一个比较常用的控制器,他提供了比较底层的数据绑定,解析等功能。

如果你对该部分内容不甚了解,可以参阅:Angular-API-type-ngModel.NgModelController

在指令内部,主要定义了当视图(UI)发生变化时,如何更新模型,当模型变化时,如何更新视图(UI)。

UI >> 模型 的监听主要依靠监听元素的click事件;

模型 >> UI 的监听主要依靠自定义实现 ngModel.$render 方法,该方法会在模型修改时,透明的调用。

详解

整个指令的核心内容就是UI和模型之间的更新逻辑(radio为例):

// model -> UI
ngModelCtrl.$render = function() {
  element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
};

//ui->model
element.on(buttonsCtrl.toggleEvent, function() {
  if (attrs.disabled) {
    return;
  }
  var isActive = element.hasClass(buttonsCtrl.activeClass);
  if (!isActive || angular.isDefined(attrs.uncheckable)) {
    scope.$apply(function() {
      ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
      ngModelCtrl.$render();
    });
  }
});

更为直接的就是使用 ngModel.$render() 方法 和 ngModel.$setViewValue() 方法。

如果让我们自己去实现这个指令,我们可能只会简单的监听click事件,并在click事件发生时,修改ngModel的值,添加删除class类而已。

而不会想到这种更为优雅的方式,这就是学习该指令可以学到的东西。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注