使用 VueJS 动态编译和挂载元素

Posted

技术标签:

【中文标题】使用 VueJS 动态编译和挂载元素【英文标题】:Dynamically compiling and mounting elements with VueJS 【发布时间】:2017-09-23 22:59:24 【问题描述】:

问题

我为 VueJS 围绕 jQuery DataTables 创建了一个轻量级包装器,如下所示:

<template>
    <table ref="table" class="display table table-striped" cellspacing="0" >
        <thead>
            <tr>
                <th v-for="(column, index) in columns">
                     column.name 
                </th>
            </tr>
        </thead>
    </table>
</template>

<script>
    export default 
        props: ['columns', 'url'],
        mounted: function () 
            $(this.$refs.table).dataTable(
                ajax: this.url,
                columns: this.columns
            );
            // Add any elements created by DataTable
            this.$compile(this.$refs.table);
        
    
</script>

我正在像这样使用数据表:

<data-table
    :columns="
        [
            
                name: 'County',
                data: 'location.county',
            ,
            
                name: 'Acres',
                data: 'details.lot_size',
            ,
            
                name: 'Price',
                data: 'details.price',
                className: 'text-xs-right',
            ,
            
                name: 'Actions',
                data: null,
                render: (row) => 
                    return &quot;\
                        <a @click='editProperty' class='btn btn-warning'><i class='fa fa-pencil'></i> Edit</a>\
                    &quot;;
                
            ,
        ]
    "
    url="/api/properties"
></data-table>

注意“操作”列的“渲染”方法。此函数运行良好并按预期呈现按钮,但是 @click 处理程序不起作用。

环顾四周,我发现了两个没有帮助的链接:

Issue 254 on the VueJS GitHub repo 为 VueJS 1.0 提供了一个解决方案(使用 this.$compile),但是这在 VueJS 2.0 中被删除了

A blog post by Will Vincent 讨论了如何在本地数据动态变化时重新渲染 DataTable,但没有提供将处理程序附加到渲染元素的解决方案

最小可行解决方案

如果渲染的元素不能被编译和挂载,那没关系,只要我可以在点击时运行DataTable 组件的方法。也许是这样的:

render: (row) => 
    return &quot;\
        <a onclick='Vue.$refs.MyComponent.methods.whatever();' />\
    &quot;;

有没有这种方法可以从 Vue 上下文之外调用方法?

【问题讨论】:

对于在渲染函数中处理事件,您使用on: section 或createElement。不是那么模板。 @RoyJ 我没有立即明白如何使用on: 部分来处理在我的组件安装后创建的子级引发的事件。充其量也许我可以收听nativeOn: 并在事件冒泡后对事件做出反应,然后尝试使用e.target 找出事件的来源,但这听起来很容易被打破。你能举个例子说明你的建议吗? 我想我只是误解了您的渲染功能在哪里发挥作用。 【参考方案1】:

这符合您的最小可行解决方案。

在您的列定义中:

render: function(data, type, row, meta) 
   return `<span class="edit-placeholder">Edit</span>`  

在您的 DataTable 组件中:

methods:
  editProperty(data)
    console.log(data)
  
,
mounted: function() 
  const table = $(this.$refs.table).dataTable(
    ajax: this.url,
    columns: this.columns
  );

  const self = this
  $('tbody', this.$refs.table).on( 'click', '.edit-placeholder', function()
      const cell = table.api().cell( $(this).closest("td") );
      self.editProperty(cell.data())
  );

Example(使用不同的 API,但想法相同)。

这是在使用 jQuery,但你已经在使用 jQuery,所以感觉没那么糟糕。

我玩了一些游戏,试图让一个组件挂载到数据表的render 函数中并取得了一些成功,但我对 DataTable API 不够熟悉,无法使其完全工作。最大的问题是 DataTable API 期望渲染函数返回一个字符串,这是......限制。 API 也非常烦人 没有为您提供当前所在单元格的引用,这似乎很明显。否则你可以做类似的事情

render(columnData)
   const container = document.createElement("div")
   new EditComponent(data: columnData).$mount(container)
   return container

此外,渲染函数以多种模式调用。我能够将一个组件渲染到单元格中,但必须使用该模式玩很多游戏等。This is an attempt,但它有几个问题。我链接它是为了让你知道我在尝试什么。也许你会取得更大的成功。

最后,您可以将组件安装到由 DataTable 呈现的占位符上。考虑这个组件。

const Edit = Vue.extend(
  template: `<a @click='editProperty' class='btn btn-warning'><i class='fa fa-pencil'></i> Edit</a>`,
  methods:
    editProperty()
      console.log(this.data.name)
      this.$emit("edit-property")
    
  
);

在您的挂载方法中,您可以这样做:

mounted: function() 
  const table = $(this.$refs.table).dataTable(
    ajax: this.url,
    columns: this.columns
  );

  table.on("draw.dt", function()
    $(".edit-placeholder").each(function(i, el)
        const data = table.api().cell( $(this).closest("td") ).data();
        new Edit(data:data).$mount(el)
    )
  )

这将在每个占位符之上渲染一个 Vue,并在绘制时重新渲染它。 Here 就是一个例子。

【讨论】:

今晚下班后我必须测试一下,但初步阅读它看起来很棒!继续为所有辛勤工作打+1,如果今晚运行将标记为正确:) 我实际上只是稍微扩展了您的解决方案。因为我的 DataTable 组件是通用的,所以我不希望“编辑属性”逻辑出现在 DataTable 组件中。所以我在dt.draw 事件上设置了this.$emit("draw"),然后我将用于安装新Edit 组件的代码放置在实际使用DataTable 显示属性的组件中

以上是关于使用 VueJS 动态编译和挂载元素的主要内容,如果未能解决你的问题,请参考以下文章

灵光一闪!帮你使用Vue,搞定无法解决的“动态挂载”

灵光一闪!帮你使用Vue,搞定无法解决的“动态挂载”

为啥 angular.js 在添加动态元素时不够聪明,无法编译 DOM?

我无法在vue路由器中使用动态导入

Vuejs 观察动态数组元素的变化

VueJS 无法挂载组件