datatable only working for the first time - datatables

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.

Related

Change v-model value without changin the actual data

So i've this data
data: () => ({
products: [
{ id: 1, name: "Prod 1", price: 2, stock: 5 },
{ id: 2, name: "Prod 2", price: 3, stock: 6 }
]
})
Template
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody>
<tr v-for="product in products" :key="product.id">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>
<input
type="text"
class="form-control"
v-model="product.price"
#paste.prevent
/>
</td>
<td>
<input
type="text"
class="form-control"
maxlength="999"
v-model="product.stock"
#paste.prevent
#keypress="onlyNumber($event)"
#input="handleInputStock($event.target.value)"
#blur="updateStock($event.target.value, product.id)"
/>
</td>
</tr>
</tbody>
</table>
So what I want is that when the user hit delete/backspace from the stock input field the value cannot be empty (blank) or it must be greater than or equal to zero. but without changing the products.stock value. this is because I need the product.stock value to compare with the changed value (stock input field) before sending to the server. So if stock value is equal to product.stock don't send to server otherwise send and update stock value.
so here's what i've done so far.
prevent the stock value empty but not working
handleInputStock(value) {
return +value.replace(/[^0-9]/g, "");
},
update stock
updateStock(stock, productId) {
const productStock = this.products.find(product => product.id == productId).stock;
if (!(stock == productStock)) {
// do ajax
}
},
onlyNumber
onlyNumber(e) {
const charCode = e.which ? e.which : event.keyCode;
if (charCode > 31 && (charCode < 48 || charCode > 57)) {
e.preventDefault();
}
},
Personally this feels like a higher level question to which your flow of product editing needs tweaking. Here is what I can think of:
User enters all the information.
User hits submit button.
Check whether of not the stock count is empty or 0.
Return an error message if it is.
Submit and update otherwise.
It might be worth looking into vuelidate that handles such validation in JavaScript. Meanwhile, we are also coming up with a tool called CRUDS DS (a WIP) that handles such situation with ease.
The best way is to create a ProductComponent and watch every product separately inside its own component, as shown below:
Product.vue
<ProductComponent
v-for="product in products"
:product="product"
:key="product.id" />
ProductComponent.vue
<template>
<tr>
<td>{{ product.name }}</td>
<td>
<input
type="text"
class="form-control"
v-model="product.price"
#paste.prevent
/>
</td>
<td>
<input
type="text"
class="form-control"
maxlength="999"
v-model="product.stock"
#paste.prevent
#keypress="onlyNumber($event)"
#blur="updateStock($event.target.value, product.id)"
/>
</td>
</tr>
</template>
<script>
export default {
props: {
product: {
type: Object,
default: {},
},
},
data: () => ({ actual_stock: "" })
// this is for handle stock cannot be empty or GTE:0
// also you dont need handleInputStock anymore
watch: {
product: {
handler(val) {
this.actual_stock = val.stock;
},
immediate: true,
},
"product.stock": function (newVal, oldVal) {
this.product.stock = +newVal;
},
},
methods: {
updateStock(stock, productId) {
if (!(stock == this.actual_stock)) {
// do ajax
}
}
}
}
</script>
If you want to handle it on parent side, you may use $emit to send an event upwards.
Can we have two versions of products? One for the server, one for v-models.
var server_products = [
{ id: 1, name: "Prod 1", stock: 5 },
{ id: 2, name: "Prod 2", stock: 6 }
]
//...
data: () => ({
products = server_products
})
updateStock(stock, productId) {
server_products.forEach((product) => {
if(product.id === productId && stock !== product.stock){
product.stock = stock
// do ajax
}
})
},
//...
If not than you can use vue's watch property so vue finds changes to the array for you.
//...
data: () => ({
products: [
{ id: 1, name: "Prod 1", stock: 5 },
{ id: 2, name: "Prod 2", stock: 6 }
]
}),
watch: {
'products': {
handler: function(newValue) {
// do ajax
},
deep: true
}
}
//...

vue js error:" For recursive components, make sure to provide the "name" option." when recursively create table

I create a dynamic HTML table by vue js which get data from server. The data includes 'columns', which is a Array with server objects including the table header(key is title), 'q', is the value type of each column generated from database table column name, 'content", the value display method. e.g content='content":'', the cell will display like ''.
HTML Part
<div id="app3">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" v-for="c in columns" v-text="c.title"></th>
</tr>
</thead>
<tbody>
<vue-cell:rows="rows" :columns="columns">
</vue-row>
</tbody>
</table>
</div>
Vue js part
<script>
var columns =[
{'q': "id",
'title': 'id',
'content":'<input type="checkbox">'
},
{'q': "type",
'title': 'Type',
'content":''
},
]
var rows=[
{'id': 1,
'type':"Server",
},
{'id': 2,
'type':"PC",
},
]
create component to loop rows and column
Vue.component('vue-cell', {
name:'inputBox',
props: {
rows: Array,
columns: Array,
},
render: function (createElement) {
var self = this
return createElement('tr', self.rows.map(function(row) {
return createElement('td', self.columns.map(function(column) {
// comlum.q = id or type ...
// column['content'] = <input type="checkbox">
//assign row.id as defalut value
if (column['content'].includes('checkbox')) {
return createElement('input', {
attrs: {
type: "checkbox",
},
domProps: {
// column.q may not be 'id' maybe type
// get the value of type or id in a row.
value: row[column.q],
}
})
} else {
return row[column.q];}
}))
}))
},
})
Create a new Vue
new Vue ({
data: function() {
return {
columns: [],
rows:[],
}
},
created: function() {
var self = this;
axios.("/web/asset-json.html").then(function (response) {
self.columns = [...response.data.columns];
self.rows = [...response.data.rows]
})
}
})
</script>

V-model does not get updated after checkbox clicked

Any idea how to resolve this problem:
in this example, the author uses vue 2.3.2 which works perfect,
new Vue({
el: '#app',
data: {
users: [{
"id": "Shad",
"name": "Shad"
},
{
"id": "Duane",
"name": "Duane"
},
{
"id": "Myah",
"name": "Myah"
},
{
"id": "Kamron",
"name": "Kamron"
},
{
"id": "Brendon",
"name": "Brendon"
}
],
selected: [],
allSelected: false,
userIds: []
},
methods: {
selectAll: function() {
this.userIds = [];
if (this.allSelected) {
for (user in this.users) {
this.userIds.push(this.users[user].id.toString());
}
}
},
select: function() {
this.allSelected = false;
}
}
})
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<h4>User</h4>
<div>
<table>
<tr>
<th>Name</th>
<th>Select All<input type="checkbox" #click="selectAll" v-model="allSelected"></th>
</tr>
<tr v-for="user in users">
<td>{{ user.name }}</td>
<td><input type="checkbox" v-model="userIds" #click="select" :value="user.id"></td>
</tr>
</table>
</div>
<span>Selected Ids: {{ userIds }}</span>
</div>
when I switch it to 2.5.16 ( <script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script> ) , the behavior is wierd:
When click the selectAll checkbox, only that checkbox checked, but when I toggle it to uncheck, all the checkboses below get checked
For consistent browser functionality, I can recommended to not use click/change on checkboxes. Instead, bind the checkbox to a value (which you've already done), and then use a watcher on the value. This way, the real value of the checkbox will always accurately represent it's state. So you'd have something like this:
<input type="checkbox" v-model="allSelected">
Vue.component({..., {
data: function() {
return {
allSelected: false,
}
}
},
watch: {
allSelected: function(val){
//Use your source of truth to trigger events!
this.doThingWithRealValue(val);
}
}
});
You're already using your component data value of allSelected as the source of truth, so you should use this source of truth as the real triggering element value, not a click. Whenever the value of allSelected changes, your code will get ran. This solves the problem without the rendering order weirdness.
As pointed out by rob in the comments and in his answer you cannot rely on #click / #input / #change to have the same behaviour in all browsers in regards to their execution order relative to the actual model change.
There is an issue at the VueJS repository with a bit more context: https://github.com/vuejs/vue/issues/6709
The better solution is to watch the model for changes and then react accordingly.
new Vue({
el: '#app',
data: {
users: [{
"id": "Shad",
"name": "Shad"
},
{
"id": "Duane",
"name": "Duane"
},
{
"id": "Myah",
"name": "Myah"
},
{
"id": "Kamron",
"name": "Kamron"
},
{
"id": "Brendon",
"name": "Brendon"
}
],
selected: [],
allSelected: false,
userIds: []
},
methods: {
selectAll: function() {
this.userIds = [];
if (this.allSelected) {
for (user in this.users) {
this.userIds.push(this.users[user].id.toString());
}
}
},
select: function() {
this.allSelected = false;
}
},
watch: {
allSelected: function () {
this.selectAll()
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h4>User</h4>
<div>
<table>
<tr>
<th>Name</th>
<th>Select All<input type="checkbox" v-model="allSelected"></th>
</tr>
<tr v-for="user in users">
<td>{{ user.name }}</td>
<td><input type="checkbox" v-model="userIds" #click="select" :value="user.id"></td>
</tr>
</table>
</div>
<span>Selected Ids: {{ userIds }}</span>
</div>

How to reload dataTables from VueJS after adding or editing?

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.

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/