Currently I have a validation where I disable the buttons depending on the numerical value as follows:
disabled = [0,2,3,5]
/* Formatting function for row details - modify as you need */
function format(d) {
// `d` is the original data object for the row
$tipoproveedor = $("#txttipoproveedor").val();
console.log(d);
let tabla = `<table cellpadding="5" cellspacing="0" style="border-collapse: separate; border-spacing: 40px 5px;">
<thead>
<tr>
<th>
Date Order
</th>
<th>
Order
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>`;
d.Factura.forEach(f => { tabla +=
`<tr>
<td>${f.DateInvoice}</td>
<td>${f.Invoice}</td>
<td>${f.Status}</td>
<td><button type="button" class="btn btn-primary" data-toggle="modal" data-target="#ModalCargaFactura" onclick="LoadInvoice('${f.PurchaseOrder}' )"`;
if($tipoproveedor != '0'){
if (disabled.indexOf(f.Estatus) > -1) {
tabla += ` disabled `;
}
}
tabla += `>Upload Documents</button></td>
<td><button type="button" class="btn btn-primary" onclick="ShowDetailsInvoice('${f.Invoice}')">Details</button></td>
</tr>`;
});
tabla += '</tbody></table>';
return tabla;
}
Where I disable the button in the values 0,2,3,5 now these values will change to strings giving the following assignment to the numeric values like this:
0 = 'None'
2 = 'Accept'
3 = 'Send'
5 = 'Delivered'
What I require now is to validate no longer with the numbers but with the character string, I hope someone can give me some guidance with this validation.
Update 1:
Based on the answer I have made the following code changing my array of values for strings as follows:
disabled = ['None','Accept','Send','Delivered']
/* Formatting function for row details - modify as you need */
function format(d) {
// `d` is the original data object for the row
$tipoproveedor = $("#txttipoproveedor").val();
console.log(d);
let tabla = `<table cellpadding="5" cellspacing="0" style="border-collapse: separate; border-spacing: 40px 5px;">
<thead>
<tr>
<th>
Date Order
</th>
<th>
Order
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>`;
d.Factura.forEach(f => {tabla +=
`<tr>
<td>${f.DateInvoice}</td>
<td>${f.Invoice}</td>
<td>${f.Status}</td>
<td><button type="button" class="btn btn-primary" data-toggle="modal" data-target="#ModalCargaFactura" onclick="LoadInvoice('${f.PurchaseOrder}' )"`;
if($tipoproveedor != '0'){
if (disabled.indexOf(f.Estatus) > -1) {
tabla += ` disabled `;
}
}
tabla += `>Upload Documents</button></td>
<td><button type="button" class="btn btn-primary" onclick="ShowDetailsInvoice('${f.Invoice}')">Details</button></td>
</tr>`;
});
tabla += '</tbody></table>';
return tabla;
}
What little I understand is that the validation no longer detects the numerical values that existed in the array disabled and for this reason it marks the error and the data is not loaded into the table.
It will explain a little more in detail, currently I have in the table the column Status where the values are shown 0,2,3,5 and the buttons are disabled or enabled depending on their value. In this case I have been forced to change these same values for strings and in order not to complicate my life much I have decided to make this change from the query with which I show the data in the table with its simple caselike this:
CASE STATUS
WHEN 0 THEN 'None'
WHEN 1 THEN 'Receipment'
WHEN 2 THEN 'Accept'
WHEN 3 THEN 'Send'
WHEN 4 THEN 'Process'
WHEN 5 THEN 'Delivered'
ELSE 'Other'
END as 'STATUS'
Save the values as an object in the form:
const disabledValues = {
0: 'None',
2: 'Accept',
3: 'Send',
5: 'Delivered',
};
Later, during a comparison, cast your Estatus into the number (adding + at the beginning) and use it in the form:
if (disabledValues[+f.Estatus]) {
tabla += ` disabled `;
}
Maybe I'm misunderstanding the question, but it sounds like you now expect .Estatus to contain a string instead of a number, and you still want the button disabling to work? If that's the case just change
disabled = [0,2,3,5]
to
disabled = ['None', 'Accept', 'Send', 'Delivered']
or even
disabled = [0, 2, 3, 5, 'None', 'Accept', 'Send', 'Delivered']
so you can handle both.
disabled = [0, 2, 3, 5, 'None', 'Accept', 'Send', 'Delivered']
/* Formatting function for row details - modify as you need */
function format(d) {
// `d` is the original data object for the row
$tipoproveedor = $("#txttipoproveedor").val();
console.log(d);
let tabla = `<table cellpadding="5" cellspacing="0" style="border-collapse: separate; border-spacing: 40px 5px;">
<thead>
<tr>
<th>
Date Order
</th>
<th>
Order
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>`;
d.Factura.forEach(f => {
tabla += `<tr>
<td>${f.DateInvoice}</td>
<td>${f.Invoice}</td>
<td>${f.Status}</td>
<td><button type="button" class="btn btn-primary" data-toggle="modal" data-target="#ModalCargaFactura" onclick="LoadInvoice('${f.PurchaseOrder}' )"`;
if($tipoproveedor != '0'){
if (disabled.indexOf(f.Estatus) > -1) {
tabla += ` disabled `;
}
}
tabla += `>Upload Documents</button></td>
<td><button type="button" class="btn btn-primary" onclick="ShowDetailsInvoice('${f.Invoice}')">Details</button></td>
</tr>`;
});
tabla += '</tbody></table>';
return tabla;
}
Initialise you array like below, where you can customize your message.
const valuesToDisable = [
{
value: 0,
note: 'None'
},
{
value: 2,
note: 'Accept'
},
{
value: 3,
note: 'Send'
},
{
value: 5,
note: 'Delivered'
}
]
Iterate through the array and display it's note accordingly.
First, define your statuses in an object, give them a value how a button is disabled:
const statuses = {
None: true,
Receipment: false,
Accept: true,
Send: true,
Process: true,
Delivered: true,
Other: false,
};
Then tune your code as:
if ($tipoproveedor != '0') {
if (statuses[f.Estatus]) {
tabla += ` disabled `;
}
}
Just a quick idea, can be optimised further:
Your array :
const disabled = [
{
id: 0,
text: 'None'
},
{
id: 2,
text: 'Accept'
},
{
id: 3,
text: 'Send'
},
{
id: 5,
text: 'Delivered'
}
];
A function to determinate the status
isButtonDisabled = function(status) {
const found = disabled.filter(d => {
return d.text === status;
});
return found.length ? true : false
}
Usage:
if ($tipoproveedor != '0'){
if (isButtonDisabled(f.Estatus)) {
tabla += ` disabled `;
}
}
After reading the question and all the answers I failed to understand why it's not working.
By saying "I have been forced to change these same values for strings" if the questioner means,
now the array disabled = [ "0", "2", "3", "5" ]
and typeof f.Estatus === "string" then the solution is obvious.
const disabled = [ "0", "2", "3", "5" ]
if(disabled.includes(f.Estatus))
tabla += "disabled"
Related
I'm having a problem in retrieving only the ids of the selected row via a checkbox in my table where the elements are dynamically created.
In the code below I have put only the one of interest.
Specifically I get the following error:
Cannot read properties of null (reading 'textContent')
.
.
.
let child = document.createElement("tr");
child.innerHTML = `
<td>${item.id}</td>
<td><img src=articoli_img/${item.image} width="150" heigth="150"></td>
td>${item.date}</td>
<td>${'<input type="checkbox" id="myCheck">'}</td>`;
table.appendChild(child);
})
}
};
function retrieveID() {
var cbs = document.querySelectorAll('#my-table input[type="checkbox"]:checked');
console.log(cbs.length);
const ids = Array.from(cbs).map(cb => cb.closest('td').nextElementSibling.textContent);
console.log(ids);
}
<table id="my-table" width="90%">
<tr>
<th>Id</th>
<th>Image</th>
<th>Date</th>
<th>Check</th>
</tr>
</table>
<br><br>
<input type="button" value="GetID" onclick="retrieveID()" />
Can you kindly help me?
You can simply store the id in a attribute of the checkbox:
child.innerHTML = `... <td>${`<input type="checkbox" id="myCheck" data-item-id="${item.id}">`}</td>`;
// ...
const ids = Array.from(cbs).map((cb) => cb.getAttribute("data-item-id"));
which will make your code look like this:
let table = document.getElementById("my-table");
const Items = [
{
id: 1,
image: "test1.jpg",
date: "2020-01-01",
},
{
id: 2,
image: "test2.jpg",
date: "2020-01-02",
},
{
id: 3,
image: "test3.jpg",
date: "2020-01-03",
},
];
Items.forEach(function (item) {
let child = document.createElement("tr");
child.innerHTML = `
<td>${item.id}</td>
<td><img src="articoli_img/${item.image}" width="150" heigth="150"></td>
td>${item.date}</td>
<td>${`<input type="checkbox" id="myCheck" data-item-id="${item.id}">`}</td>`;
table.appendChild(child);
});
function retrieveID() {
var cbs = document.querySelectorAll(
'#my-table input[type="checkbox"]:checked'
);
console.log(cbs.length);
const ids = Array.from(cbs).map((cb) => cb.getAttribute("data-item-id"));
console.log(ids);
}
<table id="my-table" width="90%">
<tr>
<th>Id</th>
<th>Image</th>
<th>Date</th>
<th>Check</th>
</tr>
</table>
<br><br>
<input type="button" value="GetID" onclick="retrieveID()" />
You have a lot of small errors in your code. i have rewritten your code a little bit. so that you are able to read out the clicked rows. you can then pull the data from the tr element yourself.
let table = document.getElementById("my-table");
let allart = [];
allart['Items'] = [{id: 1, image: 'xx', date: 2022}]
console.log(allart['Items'][0].id)
allart.Items.forEach(function(item) {
let child = document.createElement("tr");
child.setAttribute("id", "data-" +item.id);
child.innerHTML = `
<td>${item.id}</td>
<td><img src=articoli_img/${item.image} width="150" heigth="150"></td>
<td>${item.date}</td>
<td><input type="checkbox" data-ref="${item.id}" id="myCheck"></td>`;
table.appendChild(child);
})
//xmlhttp.open("GET", url, true);
//xmlhttp.send();
function retrieveID() {
var cbs = document.querySelectorAll('#my-table input[type="checkbox"]:checked');
console.log(cbs.length);
console.log(cbs[0])
let col = [];
cbs.forEach(c => {
let id = c.getAttribute('data-ref');
let data = document.querySelector('#data-' + id)
console.log(data)
// do something
})
}
<table id="my-table" width="90%">
<tr>
<th>Id</th>
<th>Image</th>
<th>Date</th>
<th>Check</th>
</tr>
</table>
<br><br>
<input type="button" value="GetID" onclick="retrieveID()" />
I would suggest that you rework your solution somewhat: do not use the selected state directly from the DOM (HTML). Instead, update the dataset based on an event handler that reacts to some user event.
The snippet below does this:
stores the checked state in the Items
the checked state is updated when the checkbox change event fires
when you get the IDs, it's simply a filter of the Items array.
Also, I updated your example as there were some errors.
const table = document.querySelector("#my-table tbody");
const btnGetId = document.getElementById('get-id')
/*.
.
.*/
const allart = {
Items: [{
id: 'id1',
image: 'fakeImage1',
date: Date.now(),
checked: false,
},
{
id: 'id2',
image: 'fakeImage2',
date: Date.now(),
checked: false,
},
],
}
allart.Items.forEach(function(item) {
let child = document.createElement("tr");
child.innerHTML = `
<td>${item.id}</td>
<td><img src="articoli_img/${item.image}" width="150" heigth="150"></td>
<td>${item.date}</td>
<td><input type="checkbox" data-itemid="${item.id}"/></td>`;
table.appendChild(child);
})
// }
// };
// reacting to selection
const updateChecked = (id, items) => {
return items.map(item => {
if (id === item.id) {
return {
...item,
checked: !item.checked
}
}
return item
})
}
// adding event handlers to the checkboxes
const cbs = document.querySelectorAll('tr input[type="checkbox"]')
cbs.forEach(cb => {
cb.addEventListener('change', function(e) {
const id = e.target.getAttribute("data-itemid")
allart.Items = updateChecked(id, allart.Items)
})
})
// xmlhttp.open("GET", url, true);
// xmlhttp.send();
// updating the event handler on GetId click
btnGetId.addEventListener('click', function() {
const selectedItems = retrieveId(allart.Items)
// here you have the items that are selected
// you can use all the data it has
console.log(selectedItems)
})
// returning the items that are selected
function retrieveId(items) {
return items.filter(({ checked }) => checked)
}
<table id="my-table" width="90%">
<thead>
<tr>
<th>Id</th>
<th>Image</th>
<th>Date</th>
<th>Check</th>
</tr>
</thead>
<tbody></tbody>
</table>
<br><br>
<input id="get-id" type="button" value="GetID" />
I'm trying to implement Server Side DataTable.
Everything goes perfectly fine up to the last rowCallback where I'm appending button to additional column for Actions (i.e. Edit/Delete).
Issue:
Here's my code.
<link href="~/Content/datatables.min.css" rel="stylesheet" /> //<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/dt-1.10.16/datatables.min.css"/>
<link href="~/Content/font-awesome.min.css" rel="stylesheet" />
<div class="container">
<div class="row" style="margin-top:25px">
<table class="table table-bordered table-responsive dataTables-list">
<thead>
<tr>
<th>
Id
</th>
<th>
Name
</th>
<th>
Actions
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
<i class="fa fa-pencil"></i>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
JavaScript:
<script src="~/Scripts/datatables.min.js"></script> //<script type="text/javascript" src="https://cdn.datatables.net/v/dt/dt-1.10.16/datatables.min.js"></script>
<script>
$(document).ready(function () {
$('.dataTables-list').DataTable({
/*Sorting*/
"bSort": true,
"aoColumnDefs": [{
'bSortable': true
}],
"processing": true, // for show progress bar
"serverSide": true, // for process server side
"ajax": {
"url": "/Home/LoadData",
"type": "POST",
"datatype": "json"
},
"aoColumns": [{
"mDataProp": "Id"
}, {
"mDataProp": "Name"
}, {
"mDataProp": "Actions"
}],
"rowCallback": function (row, data, index) {
var newBtns = '<i class="fa fa-pencil"></i> ';
// $(row).append(newBtns);
$('td:eq(2)', row).html(newBtns);
},
language: {
paginate: {
next: '»',
previous: '«'
},
emptyTable: "Der er ingen poster.",
sInfo: "Viser _START_ til _END_ af poster."
}
});
});
</script>
Controller:
[HttpPost]
public ActionResult LoadData()
{
try
{
var draw = Request.Form.GetValues("draw").FirstOrDefault();
var start = Request.Form.GetValues("start").FirstOrDefault();
var length = Request.Form.GetValues("length").FirstOrDefault();
//Find Order Column
var sortColumn = Request.Form.GetValues("columns[" + Request.Form.GetValues("order[0][column]").FirstOrDefault() + "][data]").FirstOrDefault();
var sortColumnDir = Request.Form.GetValues("order[0][dir]").FirstOrDefault();
int pageSize = length != null ? Convert.ToInt32(length) : 0;
int skip = start != null ? Convert.ToInt32(start) : 0;
int recordsTotal = 0;
var v = (from a in _db.AllRoles select a); //Table contains only two Columns - Id and Name
//SORT
if (!(string.IsNullOrEmpty(sortColumn) && string.IsNullOrEmpty(sortColumnDir)))
{
v = v.OrderBy(sortColumn + " " + sortColumnDir);
}
recordsTotal = v.Count();
var data = v.Skip(skip).Take(pageSize).ToList();
return Json(new { draw = draw, recordsFiltered = recordsTotal, recordsTotal = recordsTotal, data = data }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
throw;
}
}
The issue is due to column difference may be but I don't know how to solve it as implementing ServerSide Datatable for first time.
Thanks in advance.
I modified the below section in my Code to solve the Error.
"aoColumns": [{
"mDataProp": "Id"
}, {
"mDataProp": "Name"
}, {
"mDataProp": "Actions",
"defaultContent": '<i class="fa fa-pencil"></i> '
}],
I added defaultContent for that column as it is not getting values from Database/Sp.
P.S. Answer provided by #dee is also correct and will solve the error. Both are the Solutions to this Question.
The link in the error message provides very good information about what is the problem. You have specified three columns for the DataTable function but as you write in the comment Table contains only two Columns - Id and Name.
"aoColumns": [{
"mDataProp": "Id"
}, {
"mDataProp": "Name"
}, {
"mDataProp": "Actions"
}],
The Resolution section of the document tells what is needed to do:
If using columns ensure that you have specified exactly the number of columns that are present in the HTML for the table.
So you will need the transform the result of the query into another class which will have additional property for Actions. HTH
I have the below datatable declaration and want to display images depend on some column value (team_members). How can I declare the for loop inside columns declaration? i want this result:
var datatableVariable = $('#projects-progress').DataTable({
data: data,
columns: [
{ 'data': 'project_name' },
{ 'data': 'team_members_value' }, // to be hidden
{
//I want loop here depend on the number of team_members_value (second column above)
mRender: function (o) { return '<img src="images/img.jpg" class="avatar" alt="Avatar">';
},
/* EDIT Delete */
{
mRender: function (o) { return '<i class="fa fa-folder"></i> View <i class="fa fa-pencil"></i> Edit <i class="fa fa-trash-o"></i> Delete '; }
},
]
});
<table id="projects-progress" class="table table-striped projects">
<thead>
<tr>
<th>Project Name</th>
<th>Team Members</th>
<th>Actions</th>
</tr>
</thead>
</table>
You can use parameter render in column declaration.
{
"data": "team_members_value",
"render": function (data, type, full, meta) {
var markup = '';
for (var i = 0; i < data; i++) {
markup +='<img src="images/img.jpg" class="avatar" alt="Avatar">'
});
return markup ;
}
}
You need to do something like this:
Add render callback.
Proceed your team_members_value data to render needed markup, save it and return.
I want to match headfield values with datafield, and have to fetch corresponding value into table data.
HTML:
<table>
<thead>
<tr>
<th ng-repeat="ReportsHead in ReportsHeadList">
{{ReportsHead.headerfield = ReportsHead.headfield}}
</th>
</tr>
</thead>
<tbody>
<tr>
<td ng-repeat="ReportsData in ReportsDataList"
ng-if="ReportsHead.headerfield == ReportsData.datafield">
{{ReportsData.value}}
</td>
</tr>
</tbody>
</table>
JSON Data :
$scope.ReportsHeadList = [
{
'headfield':'Contact Name'
},
{
'headfield':'Phone'
},
{
'headfield':'Email'
}
];
$scope.ReportsDataList = {
[
{
'datafield':'Contact Name',
'value':'Gunaseelan'
},
{
'datafield':'Phone',
'value':'8122911043'
},
{
'datafield':'Email',
'value':'G#gmail.com'
}
],
[
{
'datafield':'Contact Name',
'value':'Gunaseelan'
},
{
'datafield':'Phone',
'value':'8122911043'
},
{
'datafield':'Email',
'value':'G#gmail.com'
}
]
};
Thanks in advance.
First you should parse your Json with JSON.parse(json), and assigment the result to variable. e.g:
$scope.json = JSON.parse(json).
After, you can check for match headfield and data fields with for loop or map, e.g:
for(i in headfield) {
if (i === something)
return i
}
Hope the answar will help, if not leave a comment,
Good luck!
I've used DataTable to load data from SQL Server and display to Table.
But it just loads data.
Reference: https://gyazo.com/a77f5bee9c8f4fda3be8f9d130499bbf
Not only show this message:
No records found to show
but also, select page and page size selector don't display
Normal: https://gyazo.com/3f6d5193f36b756f752bdd20523d64e0
I hope you guys can give me a kind helpings. Thanks so much
HTML:
<table class="table table-striped table-bordered table-hover table-checkable" id="datatable_products">
<thead>
<tr role="row" class="heading">
<th width="1%">
<input type="checkbox" class="group-checkable">
</th>
<th width="10%"> ID </th>
<th width="15%"> Product Name </th>
<th width="15%"> Category </th>
<th width="10%"> Price </th>
<th width="15%"> Promotion Price </th>
<th width="10%"> Quantity </th>
<th width="15%"> Date Created </th>
<th width="10%"> Status </th>
<th width="10%"> Actions </th>
</tr>
</thead>
<tbody>
</tbody>
</table>
SQL Stored Procedure:
USE [OnlineShop]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[LoadProduct]
#Start INT=0,
#PageLimit INT=10
AS
BEGIN
SET NOCOUNT ON;
SELECT p.ID, p.Name, c.Name Category, p.Price, p.PromotionPrice, (SELECT SUM(Quantity) FROM ProductSizeColor WHERE ProductID = p.ID) as 'SumQuantity', p.CreatedDate, p.[Status]
FROM Product p
LEFT JOIN ProductCategory c
ON p.CategoryID = c.ID
ORDER BY ID
OFFSET #Start ROW
FETCH NEXT #PageLimit ROWS ONLY
END
Javascript:
var handleProducts = function() {
var grid = new Datatable();
grid.init({
src: $("#datatable_products"),
onSuccess: function(grid) {
// execute some code after table records loaded
},
onError: function(grid) {
// execute some code on network or other general error
},
loadingMessage: 'Loading...',
dataTable: {
"lengthMenu": [
[10, 20, 50, 100, 150],
[10, 20, 50, 100, 150] // change per page values here
],
"pageLength": 10, // default record count per page
"ajax": {
"url": "LoadProductTest", // ajax source
},
"processing": true,
"serverSide": true,
"columns": [
{"data": "ForCheckbox"},
{"data": "ID"},
{"data": "Name"},
{"data": "Category"},
{"data": "Price"},
{"data": "PromotionPrice"},
{"data": "SumQuantity"},
{"data": "CreatedDate"},
{"data": "DisplayStatus"},
{"data": "ForAction"},
],
"order": [
[1, "asc"]
] // set first column as a default sort by asc
}
});
Controller:
public ActionResult LoadProductTest()
{
//
var draw = Request.Form.GetValues("draw").FirstOrDefault();
var start = Request.Form.GetValues("start").FirstOrDefault();
var length = Request.Form.GetValues("length").FirstOrDefault();
int pageSize = length != null ? Convert.ToInt32(length) : 0;
int skip = start != null ? Convert.ToInt32(start) : 0;
var model = new ProductDao().LoadProduct(skip, pageSize);
int totalRecords = 0;
return Json(new { draw = draw, recordsFiltered = totalRecords, recordsTotal = totalRecords, data = model } , JsonRequestBehavior.AllowGet);
}
Method Load Product:
public List<ProductViewModel> LoadProduct(int start, int pageLimit)
{
object[] sqlParams = {
new SqlParameter("#Start", start),
new SqlParameter("#PageLimit", pageLimit)
};
return _db.Database.SqlQuery<ProductViewModel>("LoadProduct #Start, #PageLimit", sqlParams).ToList();
}
I found out how to resolve this problem.
In controller, I've set totalRecords = 0. It's wrong, it should get total records from table.
So, You no need to change any thing else but write new Stored Procedure to get count of record.
Controller : change totalRecords = 0 to
int totalRecords = new ProductDao().CountProduct();
CountProduct funtion:
public int CountProduct()
{
return _db.Database.SqlQuery<int>("CountProduct").SingleOrDefault();
}
Stored Procedure to count records:
CREATE PROCEDURE CountProduct
AS
BEGIN
SELECT COUNT(*) FROM Product
END
GO