angular-bootstrap中tooltip的实现

tooltipbootstrap的一个封装插件,用于快速简便生成提示。bootstrap基于angularJS开发了angular-bootstrap包,其中的tooltip实现原理与原来基于jquerytooltip实现原理相同,但是由于angular的特性,tooltip在此基础上又有了不同的改进。

由于angular数据绑定的特性,同样angular-bootstraptooltip也带有这种能力。tooltip的文本能够随着用户操作变化,大幅降低了编写动态提示(tooltip)的难度。

生成tooltip完整流程:触发显示事件 -> 创建tooltip -> 调整tooltip位置 -> 添加动画 -> 显示tooltip -> 触发隐藏事件 -> 隐藏tooltip -> 删除tooltip

具体来讲,angular-bootstraptooltip实现分4个主要步骤,初始化配置、数据注入、生成tooltip、编译模板。

初始化配置并不难,首先我们需要一个provider(在angularprovider在配置文件中预先调用,即provider作为模块配置的API)生成初始化配置需要的接口。

provider

//TODO: 初始化tooltip的显示样式与动画效果

var defaultOptions {
  placement'top',
  placementClassPrefix'',
  animation: true,
  popupDelay0,
  popupCloseDelay0,
  useContentExp: false
};
//TODO: 设定tooltip默认的触发条件,可以选择以下五种之一,

//也可以由自己编写一个自定义的触发方式
var triggerMap {
  'mouseenter''mouseleave',
  'click''click',
  'outsideClick''outsideClick',
  'focus''blur',
  'none'''
};

var globalOptions {};


//TODO: 通过this返回API
this.options = function (value) {
  angular.extend(globalOptionsvalue);
};

this.setTriggers = function setTriggers(triggers) {
  angular.extend(triggerMaptriggers);
};

这样就可以在config文件中通过optionssetTriggers两个接口设置tooltip的默认样式和默认触发条件了。

接下来是添加模板,tooltip使用了指令的方式去编译模板,首先定义一个模板

var template = '

'

然后在模板中加入自定义的内容

var template =
  'directiveName '-popup ' +
  (options.useContentExp ?
    'content-exp="contentExp()" ' :
  'content="' startSym 'content' endSym '" '+
  'origin-scope="origScope" ' +
  '>' +
  '

';

这个过程是怎么实现的呢,事实上比较巧妙,这里使用了二次绑定的手法,tooltip分为两层作用域(scope),首层的作用域用于传递数据,即绑定数据的来源,tooltip的模板地址tooltip-templlate、模板内容tooltip-html、文本内容tooltip等等。

ttScope.origScope scope;

通过指令中的属性绑定,即

function uibATooltipTemplatePopup() {

return {

    scope: {

        originScope: '=', 

        content: '=', 

    }

……

}

}

将首层作用域“originScope”与tooltip的中的html内容“content”传递到下一层(我将它称为模板处理层),这一层的作用是使用cache的形式获取模板数据(新建一个空白的tooltip可以在控制台查看模板的结构,也可以在源代码中查找“html”的部分查看缓存的模板),然后利用html内容与数据将tooltip初始化。

初始化的过程如下:

首先申请模板:

$templateRequest(templateSrctrue)

然后传入数据:

var newScope origScope.$new();

编译模板:

var clone $compile(template)(newScopefunction(clone) {
  cleanupLastIncludeContent();
  $animate.enter(cloneelem);
});

绑定tooltip与其作用域

currentElement clone;
currentScope newScope;

绑定tooltip与其作用域的作用是在多个tooltip存在的情况下,清理上个tooltip的数据并加载当前tooltip的数据。

var cleanupLastIncludeContent = function() {
  if (previousElement) {
    previousElement.remove();
    previousElement = null;
  }

  if (currentScope) {
    currentScope.$destroy();
    currentScope = null;
  }

  if (currentElement) {
    $animate.leave(currentElement).then(function() {
      previousElement = null;
    });
    previousElement currentElement;
    currentElement = null;
  }
};

一个简单的tooltip已经完成了,接下来是给它添加上控制逻辑。

一是tooltip的位置控制,根据绑定的元素、设定好的显示方向、是否添加到body元素三者来决定tooltip的位置。

var ttPosition $position.positionElements(elementtooltipttScope.placementappendToBody);

二是触发控制,根据配置的触发条件绑定事件。

triggers {
  showshowTriggers,
  hidehideTriggers
};

triggers.show.forEach(function (triggeridx) {

if (trigger === 'outsideClick') {
  element.on('click'toggleTooltipBind);
  $document.on('click'bodyHideTooltipBind);
else if (trigger === triggers.hide[idx]) {
  element.on(triggertoggleTooltipBind);
else if (trigger) {
  element.on(triggershowTooltipBind);
  element.on(triggers.hide[idx]hideTooltipBind);
}});

最后是编译模板并生产tooltip

var tooltipLinker $compile(template);
tooltip tooltipLinker(tooltipLinkedScopefunction (tooltip) {
  if (appendToBody) {
    $document.find('body').append(tooltip);
  else {
    element.after(tooltip);
  }
});

做到这一步,tooltip已经可以正常生成和显示了,但是还没完,这只能实现一个简单的tooltip。要实现一个完整的控件,还要对此加上控制与销毁tooltip的逻辑。

管理tooltip实际上是通过管理它的父元素作用域来完成的,那么我们需要建立一个容器来盛放每个tooltip父元素的作用域。

创建一个map

function stackMap() {
  return {

    //TODO: 通过createNew来初始化一个新的map
    createNew: function() {
      var stack [];
    }

}

接下来给它加上增删查等功能:

function stackMap() {
  return {

//TODO: 通过createNew来初始化一个新的map
    createNew: function() {
      var stack [];

  return {

    add: function (key, value) {

     stack.push({

               keykey,

               valuevalue
            });

    },

    get: function (key) {},

    getKeys: function() {},

    top: function () {

            return stack[stack.length 1];

    },

    remove: function (key) {},

    removeTop: function (key) {},

    length: function () {}

  }
    }

}

在建立了这么一个tooltip专有的容器后用它来存放每次新建的tooltip的作用域:

var openedTooltips $$stackedMap.createNew();

//TODO: 通过tooltip provider的作用域生成它的子作用域,即对应tooltip的scope

var ttScope scope.$new(true);

openedTooltips.add(ttScope{
  closehide
});

通过属性值监控tooltip

if (!ttScope.isOpen) {
  showTooltipBind();
else {
  hideTooltipBind();
}

在路由变化之后删除掉多余的父元素作用域,避免造成冗余的$digest回调:

openedTooltips.remove(ttScope);

总结:主要考虑的地方在于数据传递、模板构造、加载新tooltip,清理冗余。

你可能感兴趣的:(angular-bootstrap中tooltip的实现)