caoruiy‘s blog

Wisdom outweighs any wealth

[ui-bootstrap源码解析系列03]-accordion模块

用法

accordion(手风琴) 指令构建在 collapse(折叠) 指令之上并提供一个项目列表,可折叠的主体通过单击项目的标题来折叠或展开。

每个手风琴组的主体内容被折叠到可折叠元素中。

uib-accordion 配置项

  • close-others $ C (default: true):控制在打开某个折叠元素时,折叠其他元素。
  • template-url (Default: template/accordion/accordion.html) : 你可以覆盖该组件的默认模板

uib-accordion-group 配置项

  • heading (Default: none) : 在标题上设置可点击的控制折叠展开的标头文字。
  • is-disabled $ Watch (Default: false) : 手风琴组是否禁用折叠功能
  • is-open (Default false) : 默认情况下,手风琴是否是展开的
  • template-url (Default: uib/template/accordion/accordion-group.html) : 你可以覆盖该组件的默认模板

手风琴的 heading

为了代替 uib-accordion-group 指令的 heading 属性, 你可以设置 uib-accordion-heading 指令元素,效果和 heading 相同。

如果你在 uib-accordion-group 上使用自定义模板,则需要把标题元素设置为 uib-accordion-header, 例如:<div ui-accordion-header> </div>

已知问题

要使用手风琴中的可点击元素,您必须覆盖 accordion-group 模板才能使用div元素而不是锚点元素,并在你的css中添加:cursor: pointer。这是由于浏览器将锚点元素解释为任何点击事件的目标,当某些元素(如按钮)嵌套在锚点元素内时,他会触发路由。

实例

.demo(ng-controller="demo")
    div(uib-accordion close-others="oneAtATime")
      .panel-default(uib-accordion-group heading="Static Header, initially expanded" is-open="status.isFirstOpen" is-disabled="status.isFirstDisabled")
        .panel-heading
          h4.panel-title
            a(tabindex="0" class="accordion-toggle" ng-click="toggleOpen()"   uib-accordion-transclude="heading")
              span(uib-accordion-header ng-class="{'text-muted': isDisabled}")
              | 标题
        .panel-collapse.collapse(uib-collapse="!isOpen")
        .panel-body
          | 你好
// demo ui-bootstrap
app.controller('demo', ['$scope',function($scope){
    $scope.oneAtTime = true;
    $scope.status = {
        isFirstOpen : true,
        isFirstDisabled : false
    }
}]);

源码解析

本次解析的源码版本为:Version: 2.5.0

源码参见:https://github.com/caoruiy/bootstrap/blob/master/src/accordion/accordion.js


手风琴模块accordion.js依赖于:ui.bootstrap.collapseui.bootstrap.tabindex 模块,所以在解析本模块之前,先要了解依赖的两个模块。

这两个模块的源码解析参见:

该模块下实现了4个指令:
– uibAccordion : 手风琴总指令,所有的手风琴项目都保存于此,负责手风琴项目的增删和打开当前手风琴项目时,控制是否关闭其他所有手风琴项目的功能。
– uibAccordionGroup :单个手风琴项目,依赖uibCollapse指令,并控制自己的展开折叠
– uibAccordionHeading : 依赖uibAccordionGroup,用于设置单个手风琴项目的 heading 信息
– uibAccordionTransclude : 依赖uibAccordionGroup,主要作用是把uibAccordionHeading的内容插入到合适的地方。

以及:

  • uibAccordionConfig : 常量服务

实现思路和原理

  1. 用户使用 uibAccordion 定义一个手风琴组;
  2. 单个手风琴在 uibAccordionGroup 下定义,他接受一些属性设置,为了可以设置更为复杂的heading信息,而不仅仅是 heading 属性所能设置的简单的字符串;
  3. 当存在 uibAccordionHeading 时, heading 将不会生效(准确的说是被覆盖掉了);
  4. 当使用 uibAccordionHeading 时, uibAccordionTransclude 就把 uibAccordionHeading 内的html插入到包含 uib-accordion-header 属性的元素中,并把元素内的内容删除掉,这也是 heading 属性无法生效的原因

详细解析

当把所有注释都写在源码上时,突然觉得没什么内容好讲了,所有建议去阅读加了注释的源码:https://github.com/caoruiy/bootstrap/blob/master/src/accordion/accordion.js

需要说明的是 uibAccordionHeading 指令的实现,其link方法如下:

link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
  accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
}

首先 accordionGroupCtrl 是父指令 controller属性的引用,其中定义了 setHeading 方法,uibAccordionGroup 指令的控制器:

controller: function() {
    this.setHeading = function(element) {
        this.heading = element;
    };
},

link 方法中 transclude 函数实际上是 transclude 链接函数,你可以预先为其绑定一个控制器。其返回的是指令内DOM元素的引用,返回的是一个 JQLite 对象。所有该指令可以把 uibAccordionHeading 指令内定义的DOM元素作为 heading 使用。

但是 heading 并没有真正的插入到DOM树中, 这个工作还是由 uibAccordionTransclude 来完成,默认模板中,使用 uib-accordion-transclude="heading" 来使用该指令,指令的link方法监听了元素中 uib-accordion-transclude 属性对应值的变化,也就是 heading 的变化,当 uibAccordionHeading 指令修改 heading 时,uibAccordionTransclude 指令把 heading 插入到当前元素下 包含 uib-accordion-header 属性的元素中。

这部分源码阅读的心得是:

  1. 所有操作DOM的任务都使用指令来完成

  2. 一个指令只完成一件事

点赞

发表评论

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