问题
The Issue
I've created a light-weight wrapper around jQuery DataTables for VueJS like so:
<template>
<table ref="table" class="display table table-striped" cellspacing="0" width="100%">
<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>
I'm utilizing the data table like so:
<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 "\
<a @click='editProperty' class='btn btn-warning'><i class='fa fa-pencil'></i> Edit</a>\
";
}
},
]
"
url="/api/properties"
></data-table>
Note the "render" method for the Actions column. This function runs just fine and renders the button as expected, however the @click
handler is not functional.
Looking around I've found two links which were not helpful:
Issue 254 on the VueJS GitHub repo provides a solution for VueJS 1.0 (using this.$compile
) however this was removed in VueJS 2.0
A blog post by Will Vincent discusses how to make the DataTable re-render when local data changes dynamically, but doesn't provide a solution for attaching handlers to the rendered elements
Minimum Viable Solution
If the rendered element can't be compiled and mounted, that would alright so long as I could run methods of the DataTable
component on-click. Perhaps something like:
render: (row) => {
return "\
<a onclick='Vue.$refs.MyComponent.methods.whatever();' />\
";
}
Is there any such way to call methods from outside of the Vue context?
回答1:
This meets your minimum viable solution.
In your columns definition:
render: function(data, type, row, meta) {
return `<span class="edit-placeholder">Edit</span>`
}
And in your DataTable component:
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 (uses a different API, but the same idea).
This is using jQuery, but you're already using jQuery so it doesn't feel that terrible.
I played some games trying to get a component to mount in the render
function of the data table with some success, but I'm not familiar enough with the DataTable API to make it work completely. The biggest issue was the DataTable API expects the render function to return a string, which is... limiting. The API also very irritatingly doesn't give you a reference to the cell you are currently in, which seems obvious. Otherwise you could do something like
render(columnData){
const container = document.createElement("div")
new EditComponent({data: {columnData}).$mount(container)
return container
}
Also, the render function is called with multiple modes. I was able to render a component into the cell, but had to play a lot of games with the mode, etc. This is an attempt, but it has several issues. I'm linking it to give you an idea what I was trying. Maybe you will have more success.
Finally, you can mount a component onto a placeholder rendered by DataTable. Consider this component.
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")
}
}
});
In your mounted method you could do this:
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)
})
})
}
This will render a Vue on top of each placeholder, and re-render it when it is drawn. Here is an example of that.
来源:https://stackoverflow.com/questions/43635913/dynamically-compiling-and-mounting-elements-with-vuejs