How to reload dataTables from VueJS after adding or editing? - vue.js

I'd like to know how I can use this same approach when loading data from database through an api. In the first time the dataTables loads fine. However, when I add a new record, then I need to load my dataTables again with the new record. Here's my html code:
<table class="table table-striped table-bordered table-hover" id="dataTables-eventos">
<thead>
<tr class="tbheader">
<th class="text-center">#</th>
<th>Nome do Evento</th>
<th class="text-center">Editar</th>
<th class="text-center">Deletar</th>
</tr>
</thead>
<tbody>
<tr v-for="ev in eventos" :key="ev.id" track-by="id">
<td class="text-center">{{ ev.id }}</td>
<td>{{ ev.name }}</td>
<td class="text-center"><a class="cursorpointer" v-on:click="showMessage()"><span class="glyphicon glyphicon-edit"></span></a></td>
<td class="text-center"><span class="glyphicon glyphicon-trash"></span></td>
</tr>
</tbody>
</table>
And here's my VueJS code:
mounted() {
this.getEventos();
},
methods: {
getEventos() {
axios.get('/api/eventos')
.then((response) => {
this.eventos = response.data})
.then((response) => {
$('#dataTables-eventos').DataTable({
responsive: true,
"aaSorting": [[ 1, "asc" ]],
"aoColumnDefs": [
{ "bSortable" : false, "aTargets": [0,2,3] },
{ "searchable": false, "aTargets": [2,3] }
],
language: {
url: '/js/dataTables/localization/pt_BR.json'
}
});
});
},
addNewRecord() {
axios.post('/api/eventos', { nomeEvento: this.nomeEvento });
}
So after adding (or editing) a new record on my DB how can I reload my dataTables so I can see the changes?

I just ran into a situation where I needed to use vue.js with datatables.net and had to use customized table html. I created a component that allowed me to format the table as I needed using template/v-for and refresh the datatable while preserving datatables.net functionality. I hope this helps someone down the road...
export default {
data() {
return {
dataTable: null
}
},
props: {
data: Array
},
watch: {
data() {
if (this.dataTable) {
this.dataTable.destroy();
}
this.$nextTick(() => {
this.dataTable = $("#table").DataTable({
language: {
emptyTable: "No Results Found"
}
});
});
}
}
}

You can do it without reloading all the data.
Just add this.nomeEvento to array eventos after posting to server.

Related

Load More Data On Scroll With Vue And Vuex

I would like to ask how can I display more data by using Vue and vuex. all data stored in vuex-store management already. From State management now I want to load more data on scrolling.
I found online solution by ajax. but I need to loading form state management (Vuex).
This is my Vue template:
<template>
<div>
<div class="panel panel-default">
<div class="panel-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<tr>
<th>Name - Number of Products: <span style="color: red"> {{products}} </span></th>
<th width="100"> </th>
</tr>
</tr>
</thead>
<tbody v-if="isLoaded">
<tr v-for="company, index in companies">
<td>{{ company.name }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
export default {
data: function () {
return { }
},
computed: {
companies(){
return this.$store.getters['exa1Company/getProducts'];
},
products(){
return this.$store.getters['exa1Company/countProducts'];
}
},
mounted() {
this.$store.dispatch('exa1Company/indexResource');
}
}
</script>
My vuex store file is partial for simplicity
export const getters = {
countProducts(state) {
return state.list.data.length;
},
getProducts(state) {
return state.list.data;
},
getTodoById: (state) => (id) => {
return state.list.data.find(tod => tod.id === id)
}
};
export default {
namespaced: true,
state: customerState,
getters,
actions,
mutations,
};
something like this should work. use companiesLoaded in the template, and increase page when scrolled to bottom. I hope this helps.
data: function () {
return {
page: 1,
perPage: 20
}
},
computed: {
companies(){
return this.$store.getters['exa1Company/getProducts'];
},
companiesLoaded(){
return this.companies.slice(0, this.page * this.perPage)
},
...

:class not updating when clicked in vuejs v-for

I want to add a css class to a item in v-for when the td in clicked
<template>
<div>
<h1>Forces ()</h1>
<section v-if="errored">
<p>We're sorry, we're not able to retrieve this information at the moment, please try back later</p>
</section>
<section v-if="loading">
<p>loading...</p>
</section>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th
#click="orderByName = !orderByName">Forces</th>
</tr>
<th>Delete</th>
</thead>
<tbody>
<tr v-for="(force, index) in forces" #click="completeTask(force)" :class="{completed: force.done}" v-bind:key="index">
<td>
<router-link :to="{ name: 'ListForce', params: { name: force.id } }">Link</router-link>
</td>
<th>{{ force.done }}</th>
<th>{{ force.name }}</th>
<th><button v-on:click="removeElement(index)">remove</button></th>
</tr>
</tbody>
</table>
<div>
</div>
</div>
</template>
<script>
import {APIService} from '../APIService';
const apiService = new APIService();
const _ = require('lodash');
export default {
name: 'ListForces',
components: {
},
data() {
return {
question: '',
forces: [{
done: null,
id: null,
name: null
}],
errored: false,
loading: true,
orderByName: false,
};
},
methods: {
getForces(){
apiService.getForces().then((data, error) => {
this.forces = data;
this.forces.map(function(e){
e.done = false;
});
this.loading= false;
console.log(this.forces)
});
},
completeTask(force) {
force.done = ! force.done;
console.log(force.done);
},
removeElement: function (index) {
this.forces.splice(index, 1);
}
},
computed: {
/* forcesOrdered() {
return this.orderByName ? _.orderBy(this.forces, 'name', 'desc') : this.forces;
}*/
},
mounted() {
this.getForces();
},
}
</script>
<style>
.completed {
text-decoration: line-through;
}
</style>
I want a line to go through the items when clicked, but the dom doesn't update. If I go into the vue tab in chrome I can see the force.done changes for each item but only if I click out of the object and click back into it. I'm not to sure why the state isn't updating as it's done so when I have used an click and a css bind before.
I've tried to make minimal changes to get this working:
// import {APIService} from '../APIService';
// const apiService = new APIService();
// const _ = require('lodash');
const apiService = {
getForces () {
return Promise.resolve([
{ id: 1, name: 'Red' },
{ id: 2, name: 'Green' },
{ id: 3, name: 'Blue' }
])
}
}
new Vue({
el: '#app',
name: 'ListForces',
components: {
},
data() {
return {
question: '',
forces: [{
done: null,
id: null,
name: null
}],
errored: false,
loading: true,
orderByName: false,
};
},
methods: {
getForces(){
apiService.getForces().then((data, error) => {
for (const force of data) {
force.done = false;
}
this.forces = data;
this.loading= false;
console.log(this.forces)
});
},
completeTask(force) {
force.done = ! force.done;
console.log(force.done);
},
removeElement: function (index) {
this.forces.splice(index, 1);
}
},
mounted() {
this.getForces();
}
})
.completed {
text-decoration: line-through;
}
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<div>
<h1>Forces ()</h1>
<section v-if="errored">
<p>We're sorry, we're not able to retrieve this information at the moment, please try back later</p>
</section>
<section v-if="loading">
<p>loading...</p>
</section>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th
#click="orderByName = !orderByName">Forces</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr v-for="(force, index) in forces" #click="completeTask(force)" :class="{completed: force.done}" v-bind:key="index">
<th>{{ force.done }}</th>
<th>{{ force.name }}</th>
<th><button v-on:click="removeElement(index)">remove</button></th>
</tr>
</tbody>
</table>
<div>
</div>
</div>
</div>
The key problem was here:
this.forces = data;
this.forces.map(function(e){
e.done = false;
});
The problem here is that the property done is being added to the data after it has been made reactive. The reactivity observers get added as soon as the line this.forces = data runs. Adding done after that counts as adding a new property, which won't work.
It's also a misuse of map so I've switched it to a for/of loop instead.
for (const force of data) {
force.done = false;
}
this.forces = data; // <- data becomes reactive here, including 'done'
On an unrelated note I've tweaked the template to move the Delete header inside the top row.
Make sure force.done is set to false in data before initialization so it is reactive.
data() {
return {
force:{
done: false;
}
}
}
If force exists but has no done member set, you can use Vue.set instead of = to create a reactive value after initialization.
Vue.set(this.force, 'done', true);

datatable only working for the first time

function SearchUser(url, pageIndex) {
var table;
table = $('#UsersTable').DataTable({
serverSide: true,
retrieve:true,
ajax: {
url: url,
type: "POST",
data: { username: $('#Username').val(), email: $('#Email').val(), companyID: $('#LifeCompanies').val(), page: pageIndex, isLocked: $('#Locked').is(':checked') }
},
columns: [
{ "data": "UserId" },
{ "data": "Username" },
{ "data": "Email" },
{ "data": "IsLockedOut" },
{
"render": function (data, type, full, meta) {
return '<span>wahoo</span>';
}
}
]
});
}
<div id="search">
<h3>Search:</h3>
#using (Html.BeginForm("Search","Admin"))
{
<div>
<table width="500px">
<thead>
<tr>
<td>Username:</td>
<td>#Html.TextBox("Username")</td>
</tr>
<tr>
<td>Email:</td>
<td>#Html.TextBox("Email")</td>
</tr>
<tr>
<td>Locked:</td>
<td>#Html.CheckBox("Locked")</td>
</tr>
<tr>
<td colspan="2">
<div id="searchForm">
#Html.Partial("SearchBuild")
</div>
</td>
</tr>
<tr>
<td colspan="2"><input type="button" value="Search" onclick="SearchUser('/UserManagement/admin/Search',0)"/></td>
</tr>
</thead>
</table>
</div>
}
</div>
Hi the code above uses the datatables jquery plugin.
It seems to only work for the first time I use that function. The second time, it appears to hit the javascript but doesn't ever retrive any data from my mvc controller.
However if I use destroy instead of retrieve it works perfectly fine.
If I don't use retrieve or destroy, I get the "can't reinitialise table" error.
That function is just called by a button I click.
You need to modify your code to initialize your table and reload it separately.
Also it's not possible to modify initialization options via API unless you destroy and recreate the table.
You need another way to pass pageIndex variable. For example, you can create a hidden input with id PageIndex and set and retrieve value from there.
For example:
function initTable(url) {
var table;
table = $('#UsersTable').DataTable({
serverSide: true,
retrieve:true,
ajax: {
url: url,
type: "POST",
data: {
username: $('#Username').val(),
email: $('#Email').val(),
companyID: $('#LifeCompanies').val(),
page: $('#PageIndex').val(),
isLocked: $('#Locked').is(':checked')
}
},
columns: [
{ "data": "UserId" },
{ "data": "Username" },
{ "data": "Email" },
{ "data": "IsLockedOut" },
{
"render": function (data, type, full, meta) {
return '<span>wahoo</span>';
}
}
]
});
}
To search again you just need to call $('#UsersTable').DataTable().ajax.reload() API method.
If URL will change between searches use $('#UsersTable').DataTable().ajax.url(newurl).load() API method instead.

Is it possible to use a component property as a data variable?

I've copied my current code below. I'm trying to dynamically generate table headers depending on what type I pass as a prop to my tables component (standings and status are what I have as the data arrays in my example).
I've accomplished this by using the if statement in the header computed value to return the proper list.
However I'd like to not have to add additional if statements for every type.
Is there a way that I can leverage the type prop to bind directly to the matching data?
<div id="root" class="container">
<tables type="stats">
</tables>
</div>
Vue.component('tables', {
template: `
<table class="table">
<thead>
<tr>
<th v-for="headers in header">{{ headers }}</th>
</tr>
</thead>
<tfoot>
<tr>
<th v-for="footers in header">{{ footers }}</th>
</tr>
</tfoot>
<tbody>
<tr>
<th>1</th>
<td>Leicester City <strong>(C)</strong>
<slot></slot>
</tr>
</tbody>
</table>
`,
data () {
return {
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
};
},
computed: {
header() {
if (this.type == 'standings') {
return this.standings;
} else {
return this.stats;
}
}
},
props: {
type: { required: true }
}
});
If you change up your data structure slightly, you can remove your if statements.
data () {
return {
types:{
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
}
};
},
computed: {
header() {
return this.types[this.type]
}
},
props: {
type: { required: true }
}
Here is an example.
Vue.component('tables', {
template: `
<table class="table">
<thead>
<tr>
<th v-for="headers in header">{{ headers }}</th>
</tr>
</thead>
<tfoot>
<tr>
<th v-for="footers in header">{{ footers }}</th>
</tr>
</tfoot>
<tbody>
<tr>
<th>1</th>
<td>Leicester City <strong>(C)</strong>
<slot></slot>
</td>
</tr>
</tbody>
</table>
`,
data () {
return {
types:{
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
}
};
},
computed: {
header() {
return this.types[this.type]
}
},
props: {
type: { required: true }
}
});
new Vue({
el: "#root"
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="root" class="container">
<tables type="stats">
</tables>
</div>

Passing multiple row data from JQuery Datatables into ASP.NET 5 / MVC 6 view using a submit all button

I have an ASP.NET 5 MVC application. I am using JQuery Datatables downloaded from Datatables.net for rendering my grid.
I want to be able to select multiple records on the grid and through the click of one button to be able to update the status feild on all my records. I can't figure out how to pass the information from javascript to my MVC controller through the view. Here is what I have so far.
View Code
<table class="display " id="ExpenseTable">
<thead>
<tr>
<th></th>
<th>
#Html.DisplayNameFor(model => model.ApplicationUserid)
</th>
<th>
#Html.DisplayNameFor(model => model.ExpenseDate)
</th>
... </tr>
</thead>
<tbody>
#foreach (var item in (IEnumerable<Expense>)ViewData["Expenses"])
{
<tr>
<td></td>
<td>
#Html.DisplayFor(modelItem => item.User.FullName)
</td>
<td>
#Html.DisplayFor(modelItem => item.ExpenseDate)
</td>
....
</tr>
}
</tbody>
</table>
#section Scripts {
<script>
//script is not complete
$(document).ready(function() {
var table = $('#ExpenseTable').DataTable({
dom: 'Bfrtip',
buttons: [
{
text: 'Approve All Selected Expenses',
action: function () {
}
}
],
columnDefs: [{
orderable: false,
className: 'select-checkbox',
targets: 0
} ],
select: {
style: 'multiple',
selector: 'td:first-child'
},
order: [[1, 'asc']],
} );
} );
I want to do something like this in the Controller
public IActionResult ApproveMany(IEnumerable<Expense> model)
{
foreach (var item in model)
{
item.Status = "Approved";
_context.Update(item);
_context.SaveChanges();
}
return RedirectToAction("StaffExpense"); ;
}
What I need help with is how I can take the button I have and when it is pushed take a collection of all the expense items that the user has selected on the grid, and then pass that collection into my controller so the edited changes can be pushed back to the database.
$('#updateButton').click(function ()
{
console.log("Selected rows: "+ table.rows('.selected').data().length);
var data = $.map(table.rows('.selected').data(), function (item)
{
console.log(item)
return item;
});
//Call your MVC Controller/API to do the update (pass data)
addData(data);
});
function addData(data)
{
//Add your controller name to url
/*POST*/
$.ajax({
url: '/Controller/ApproveMany',
dataType: "json",
type: "POST",
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
cache: false,
success: function (result)
{
alert(result);
},
error: function (xhr)
{
alert('error');
}
})
}
This link would be helpful for you to get the desired solution
http://www.gyrocode.com/articles/jquery-datatables-checkboxes/