Element: [Feature Request] 表格组件自定义模板无法统一调用一个popover组件

Created on 27 Jan 2018  ·  16Comments  ·  Source: ElemeFE/element

Element UI version

2.0.11

OS/Browsers version

macOS 10.12.6 / chrome 63.0.3239.132

Vue version

2.5.13

Reproduction Link

https://jsfiddle.net/KumaCool/r8xcumy9/1/

Steps to reproduce

当我使用表格组件时,里面的数据需要调用弹出窗.
官方给的DEMO是在每个数据处引入弹窗组件.
但我表格里的数据量很大,单独绑定不现实.
我使用v-popover:popover指令对每个数据进行统一的绑定但是无效的.

What is Expected?

希望可以在表格中对数据进行v-popover:popover指令的统一绑定.

What is actually happening?

表格中对数据使用v-popover:popover绑定外部的POPOVER组件无效.

popover stale feature request

Most helpful comment

```
pop.updatePopper()
pop.referenceElm = event.target
pop.showPopper = true

this.$nextTick(() => {
pop.popperJS._reference = event.target
pop.popperJS.state.updateBound()
})

All 16 comments

你好,我刚才试了下,这样做才是正确的,不知道符不符合你的意思?

https://jsfiddle.net/r8xcumy9/2/

感谢回复.
可能我没表述清楚,抱歉以下描述中可能一些名词使用上不太专业.
popover只要引入一次标签就会创建一个相关容器.
如果我在el-table-column中使用该标签那么数据循环后会生成许多个对应的popover容器
我觉得这种开销是不必要的.
我希望在el-table外部只使用一个popover模板,在el-table-columen中自定义内容时直接使用popover提供的指令操作外部的popover.
类似事件委托这种吧.
例:

<!-- 统一使用的弹窗模板 -->
  <el-popover ref="popover" placement="top-start" title="表格弹窗" trigger="hover" content="由表格内数据触发"></el-popover>
  <el-table :data="tableData">
    <el-table-column label="name">
      <template slot-scope="scope">
          <!-- 指令触发外面的popover -->
          <span popover:popover>test</span>
      </template>
    </el-table-column>
  </el-table>

每一个弹窗触发按钮,都会在body里生成一批对应的弹窗dom。我的浏览器会卡死。
我希望多个按钮共用一个弹窗,因为这些按钮的功能相同,只不过是位置不同,这样浏览器开销少不会卡死。

遇到了同样的问题,如何解决那?而且怎样在 el-popover 里面 去关闭 这个容器的显示那?是否应该有一个冒泡的选项 来关闭这个容器

不用el-popover,自己写个div,表格内容绑定鼠标方法来显示外部div,我是这么解决的

试试在外面用el-popover,注册ref,在表格template中注册hover/click事件,
获取popover实例并调用UpdatePopper() 让popper创建 然后更新绑定popperJS.updateBound就可以达到复用了

```
pop.updatePopper()
pop.referenceElm = event.target
pop.showPopper = true

this.$nextTick(() => {
pop.popperJS._reference = event.target
pop.popperJS.state.updateBound()
})

Popover 组件使用了 popper.js

const pooper = new Popper(reference, popperNode)

从上面可以看出:一个 reference 对应一个 popperNode。

@yxmg 给出的方案是直接修改 popper.js 中的 _reference。根据他的实践,该方案可行的。但是在官方文档中,并没有看类似的用法。在最新版的的 popper.js 中,_reference 已经变成了 reference。至于还会不会导致其他的问题,这个就不好说了。

用了上面的方案之后点击别处无法触发隐藏pop

试试在外面用el-popover,注册ref,在表格template中注册hover/click事件,
获取popover实例并调用UpdatePopper() 让popper创建 然后更新绑定popperJS.updateBound就可以达到复用了

哈哈哈哈思否的老哥

@yxmg 帮上大忙了!

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@yxmg 请问使用你的那个方法后,popover显示了,如何点击空白区域再隐藏呢,是要自己写方法嘛,谢谢
还有就是你们popover的位置对嘛

需要监听它的失焦事件

给个早期的应用案例.不保证现在的可用性,当时是做成了mixin
element-ui 当时版本是 2.2.2

<template>
<div>
    <el-popover ref="pop" trigger="click" placement="left-start" popper-class="popoverInnter">
        {{content}}
    </el-popover>
    <div @click="popoverContext"></div>
</div>
</template>
export default {
    data() {
        return {
            // 弹窗显示状态
            popShow: false,
            // 弹窗聚焦状态
            _focus: false,
            // 弹窗延时器
            _focusTimeout: null
        }
    },
    methods: {
        // 触发弹窗
        popoverContext(e) {
            this.showPopper(e)
            this._focus = true
            this.$nextTick(() => {
                let el = document.querySelector('.popoverInnter')
                el.setAttribute('tabindex', -1)

                // 弹层
                el.addEventListener('mouseover', () => {
                    clearTimeout(this._focusTimeout)
                    this._focus = true
                })
                el.addEventListener('mouseout', this.focusTimeout)
                // 主体
                e.target.addEventListener('mouseover', () => {
                    clearTimeout(this._focusTimeout)
                    this._focus = true
                })
                e.target.addEventListener('mouseout', this.focusTimeout)
                el.addEventListener('blur', this.hidePopper)
                el.focus()
            })
        },
        // 显示弹窗
        showPopper(e) {
            let pop = this.$refs.pop
            pop.referenceElm = e.target
            pop.updatePopper()
            pop.showPopper = true
            this.popShow = true
            this.$nextTick(() => {
                pop.popperJS._reference = e.target
                pop.popperJS.state.updateBound()
            })
        },
        // 延时操作
        focusTimeout() {
            this._focus = false
            this._focusTimeout = setTimeout(this.hidePopper, 800)
        },
        // 关闭弹窗
        hidePopper(e) {
            if (this._focus) return
            let pop = this.$refs.pop
            pop.showPopper = false
            this.popShow = false
        }
    }
}

@KumaCool,非常牛X,多谢多谢

遇到同样的问题了,我用了一个障眼法。
首先我们希望:

  1. 不改变 el-popvoer 的原有使用方式。
  2. 达到复用单个 popover 的目的。

所以思路就是,既然真实的触发按钮只有一个,即 slot="reference" 标记的那个,那我们就在其他地方使用占位的假按钮,当点击假按钮的时候,把真按钮(外观保持一致)“搬运”到合适的位置,同时隐藏假按钮,在真按钮渲染完成后,手动触发真按钮的事件,使得 el-popover 内部也能够获取到相同的位置信息。

举个例子:

<template>
<div>
  <el-table :data="tableData">
      <el-table-column label="name">
        <template slot-scope="scope">
          <span v-show="scope.row.id !== currentId" @click="showPop(scope.row.id)">fake</span>
          <span v-show="scope.row.id === currentId" :id="`pop-anchor-${scope.row.id}`"></span>
        </template>
      </el-table-column> 
  </el-table>


  <el-popover
    placement="top"
    width="30%"
    trigger="click"
    v-if="triggerSelector">
      复用业务代码 {{ currentId }}
      <span ref="realTrigger" slot="reference" v-dom-portal="triggerSelector">real</span>
  </el-popover>
</div>
</template>

<script>
export default {
  data() {
    return {
      triggerSelector: '',
      currentId: null,
      tableData: [{
        name: 'test1',
        id: 1,
      }, {
        name: 'test2',
        id: 2,
      }],
    };
  },
  methods: {
    showPop(id) {
      this.triggerSelector = `#pop-anchor-${id}`;
      this.currentId = id;
      setTimeout(() => {
        this.$refs.realTrigger.click();
      }, 0);
    },
  },
};
</script>




注:这里的“搬运动作”用到了一个第三方的指令 vue-dom-portal ,也可以自己实现。

Was this page helpful?
0 / 5 - 0 ratings