I try to bind the data from a database tabel to an autocomplete edit field.
But somehow I don't succeed and need help.
I have the data on another webpage already in a dropdown combo. How can I access this data or replicate the funcionality for the edit field?
Thx for any hint.
-Adrian
I want the data from the db show up where I am using the country arry right now:
/*An array containing all the country names in the world:*/
var countries = ["Afghanistan","Albania","Algeria","Andorra","Angola","Anguilla","Antigua & Barbuda","Argentina","Armenia","Aruba","Australia","Austria","Azerbaijan","Bahamas","Bahrain","Bangladesh","Barbados","Belarus","Belgium","Belize","Benin","Bermuda","Bhutan","Bolivia",...];
/*initiate the autocomplete function on the "myInput" element, and pass along the countries array as possible autocomplete values:*/
autocomplete(document.getElementById("myInput"), countries);
</script>
And to get the code from the database the following code seems to be used in another file of the project:
/**
* Loads skills into the table.
* #param {array} skills An array of skills.
*/
loadTable: function(skills) {
if(typeof skills != "object") skills = [];
if ( $.fn.dataTable.isDataTable( '#dyn_skills' ) ) {
$('#dyn_skills').DataTable().destroy();
}
var t = $("table.skills");
// remove tr elements which do not have
// the class "head", "template" or "status"
$("tr:not(.head,.template,.status)", t).remove();
// no skills found
if(! skills.length) {
$("tr.status td", t).empty().append(_("No skills found."));
$("tr.status td", t).show();
} else {
$("tr.status", t).remove();
}
if(skills.length) {if ( $.fn.dataTable.isDataTable( '#dyn_skills' ) ) {
DynTable = $('#dyn_skills').DataTable();
}
else {
DynTable = $('#dyn_skills').DataTable( {
"data" : skills,
"columns" : [
{ "data" : "id", "title" : _("Id") },
{ "data" : "preferredLabel" , "title" : _("Name") },
{ "data" : "description" , "title" : _("Description") }
],
"lengthMenu": [ 5, 10, 25, 50],
stateSave: true,
"language": {
"decimal": "",
"emptyTable": _("No data available in table"),
"info": _("Showing _START_ to _END_ of _TOTAL_ entries"),
"infoEmpty": _("Showing 0 to 0 of 0 entries"),
"infoFiltered": _("(filtered from _MAX_ total entries)"),
"infoPostFix": "",
"thousands": ",",
"lengthMenu": _("Show _MENU_ entries"),
"loadingRecords": _("Loading..."),
"processing": _("Processing..."),
"search": _("Search:"),
"zeroRecords": _("No matching records found"),
"paginate": {
"first": _("First"),
"last": _("Last"),
"next": _("Next"),
"previous": _("Previous")
},
"aria": {
"sortAscending": _(": activate to sort column ascending"),
"sortDescending": _(": activate to sort column descending")
}
}
} );
//read from url parameter and set datatables search value
$.urlParam = function(name){
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results==null) return null;
else return results[1] || 0;
}
var datatablesfilter = $.urlParam('datatablesfilter');
if(datatablesfilter == null) DynTable.search("").draw();
else DynTable.search(datatablesfilter).draw();
}
}
// On click the selected row gets the class "selected_row"
// The class "selected_row" is highlighted via CSS
// and used as a row selection marker for performing any action "edit" or "delete"
$('#dyn_skills tbody').on( 'click', 'tr:not(.status)', function () {
$("table#dyn_skills tr.selected_row").removeClass('selected_row');
$(this).addClass('selected_row');
event.stopPropagation();
} );
// binding event button #button_edit
// Read the data from the marked row and call the API function
$("button#button_edit").click( function () {
if (! $("table#dyn_skills tr.selected_row").length ){
alert(_("Please select a row from the table!!!"));
}
else
{
var skill_id = $("table#dyn_skills tr.selected_row td:eq(0)").text();
var skill_name = $("table#dyn_skills tr.selected_row td:eq(1)").text();
var skill_description = $("table#dyn_skills tr.selected_row td:eq(2)").text();
KPP.loadDialog(".template.skill-edit-dialog", null, function(self) {
$(".skill-id", self).empty().append(skill_id);
$(".skill-name", self).val(skill_name);
$(".skill-description", self).empty().append(skill_description);
$(".skill-send", self).click(function() {
KPP.Skill.edit(skill_id, {
"name": $(".skill-name", self).val(),
"description": $(".skill-description", self).val()
});
});
$(".skill-name", self).focus(); // set focus
});
}
} ); // end of button_edit
Related
I created a Tabulator table which has a custom action button to execute an action and then updates the row on which the button was clicked. This it does successfully.
What I want to do is to also dynamically remove the row's link/buttonthat was clicked - only for the row whose button was clicked - as soon as the row data is updated. I've tried to resolve this for hours and checked the tabulator documentation, and I still don't know how I can go about doing this.
Your help is much appreciated.
The code is as follows:
var tableData =[
{
"id":30,
"position":201,
"category":"onel",
"name":"One Thing",
"completed_at":null,
"created_by_email":"",
"completed_by":""
},
{
"id":31,
"position":202,
"category":"onel",
"name":"Two things",
"completed_at":null,
"created_by_email":"",
"completed_by":""
},
{
"id":32,
"position":203,
"category":"onel",
"name":"Three things",
"completed_at":null,
"created_by_email":"",
"completed_by":""
},
{
"id":33,
"position":204,
"category":"onel",
"name":"Four things",
"completed_at":null,
"created_by_email":"",
"completed_by":""
},
{
"id":34,
"position":205,
"category":"onel",
"name":"Five things",
"completed_at":null,
"created_by_email":"",
"completed_by":""
}
];
var actButton = (cell, params, onRendered) => {
var myId = cell.getRow().getData().id;
clickFunction = (p_id) => {
cell.getTable().updateData(
[
{ id: p_id
, completed_at: '2021-12-31 13:59:00'
, completed_by: 'Womble'
, completed_by_email: 'wimbledon#common.org.uk'
}
]
// <--- here, I want to remove this row's action button as defined below
);
};
/**
* This renders the buttons at initialisation but doesn't respond to dynamic in-situ data-changes
*/
if ( null == cell.getRow().getData().completed_at ) {
return "<a href='javascript: clickFunction(" + myId + ")' data-toggle='tooltip' title='Execute'><i class='fas fa-camera' style='color:#9691ce;'></i></a>";
} else {
return "";
}
};
var myTable = new Tabulator(
"#divTable",
{
data: tableData,
columns: [
{title:"Position", field:"position"},
{title:"Category", field:"category"},
{title:"Name", field:"name"},
{title:"Completed", field:"completed_at", formatter:"datetime", formatterParams:{inputFormat:"YYYY_MM_DD HH:mm:ss", outputFormat:"DD/MM/YYYY HH:mm:ss", invalidPlaceholder:""} },
{title:"By", field:"completed_by", sorter:"string"},
{title:"Email", field:"completed_by_email", sorter:"string"},
{title:"Action", field:"id", sortable:false, formatter:actButton},
],
layout: "fitColumns",
pagination: "local",
paginationSize: "15",
tooltips:true,
tooltipsHeader:true,
reactiveData:true, //turn on data reactivity
}
);
I was looking to the Tabulator component object model for the solution, when it was best answered by the DOM.
Assumptions:
the action button column is assigned the field 'id' - this doesn't affect the dynamic table's layout or interaction
Using a fragment of the above code as a starting point, the solution would be:
var actButton = (cell, params, onRendered) => {
var myId = cell.getRow().getData().id;
clickFunction = (p_id) => {
cell.getTable().updateData(
[
{ id: p_id
, completed_at: '2021-12-31 13:59:00'
, completed_by: 'Womble'
, completed_by_email: 'wimbledon#common.org.uk'
}
]
).then (
() => {
/**
* Remove the action button
*/
[...document.querySelectorAll("div.tabulator-cell")]
.filter(item => item.getAttribute('title')==p_eventId)[0]
.childNodes[0].remove();
}
);
};
/**
* This renders the buttons at initialisation but doesn't respond to dynamic in-situ data-changes
*/
if ( null == cell.getRow().getData().completed_at ) {
return "<a href='javascript: clickFunction(" + myId + ")' data-toggle='tooltip' title='Execute'><i class='fas fa-camera' style='color:#9691ce;'></i></a>";
} else {
return "";
}
};
My objective is to carry multiple parameters in same row but from different column. In my case, each row contain of 7 columns. but only 3 parameters that I need to pass to btnApprove1 function. This function will appoint to other API which require all that 3 parameters.
So how to carry service_id, project_id and staff_id into btnApprove function when clicked?
columns: [
{ data : "service_id", "className": "text-center" },
{ data : "project_id", "className": "text-center" },
{ data : "staff_id", "className": "text-center" },
{ data : "status", "className": "text-center",
render: function(data){
if (data == "1001") {
return "<span onclick='btnApprove(""+data+"")'</span>";
}
else {
return data;
}
}
},
{ data : "lorry", "className": "text-center" },
{ data : "car", "className": "text-center" },
{ data : "van", "className": "text-center" }
]
function btnApprove(service_id, project_id, staff_id){
console.log(service_id, project_id, staff_id)
var params = {
"service_id": service_id,
"project_id": project_id,
"staff_id": staff_id
};
params = JSON.stringify(params);
$.ajax ({
...
});
}
According to the jQuery Datatable documentation for the column cell render() function, it takes four parameters, the third parameter is called row, and it's an array of values each representing the value of the column with the respective index. You can use this parameter to pass the right values to your btnApprove function.
In the code block below, I make use of destructuring on the row array to only get the first three values you need. Template literals also help make string HTML more readable.
render: function(data, type, [service_id, project_id, staff_id]) {
if (data === '1001') {
return `
<span onclick="btnApprove('${service_id}', '${project_id}', '${staff_id}')">
${data}
</span>
`;
}
return data;
}
To make sure that your <span> click event has access to btnApprove when used as a string like you have, it needs to be declared globally, on thing you can do is to change the definition of the function from:
function btnApprove(service_id, project_id, staff_id) {
To:
btnApprove = function(service_id, project_id, staff_id) {
I just solve this issue by applying this.
{ data : "status", "className": "text-center",
fnCreatedCell: function (nTd, sData, oData, iRow, iCol) {
if (oData.status == "1001") {
var stat = '<span class="badge badge-warning font-11">Approval</span><br/>';
$(nTd).html("<span onclick='btnApprove(""+oData.service_id+"", ""+oData.project_id+"", ""+oData.staff_id+"")'>"+stat+"</span>");
}
else if (oData.status == "1000") {
var stat2 = '<span class="badge badge-primary font-11">Registered</span>';
$(nTd).html("<span onclick='btnNotApprove(""+oData.service_id+"", ""+oData.project_id+"", ""+oData.staff_id+"")'>"+stat2+"</span>");
}
}
},
Here is my js file. I 've done everything in the server side. And implemented all basic and some advance feature of Datatable plugins. But the search function is case sensitive. If I search for "oil" it shows oil only but not OIL.
$(document).ready(function () {
var oTable = $('#myDataTable').dataTable({
"bServerSide": true,
"sPaginationType": "full_numbers",
"sAjaxSource": "/DB/AjaxOil",
"bProcessing": true,
"Search": {
"caseInsensitive": true
},
"aoColumns": [
{
"sName": "OilId",
"aTargets": [0], //Edit column
"mData": "OilId",
"bSearchable": false,
"bSortable": false,
"mRender": function (data, type, full) {
var id = full[0]; //row id in the first column
return "<a>"+id+"</a>";
}
},
{ "sName": "CommonName" },
{ "sName": "BotanicalName" },
{ "sName": "PlantParts" },
{ "sName": "Distillation" }
],
"columnDefs": [
{
"targets": [0],
className: "hide_column",
"searchable": false
}
]
});
});
And Here is my ajax function
public ActionResult AjaxOil(JQueryDataTableParamModel param)
{
IEnumerable<Oil> allOils = _context.Oils.ToList();
IEnumerable<Oil> filteredOils;
if (!string.IsNullOrEmpty(param.sSearch))
{
filteredOils = allOils
.Where(c => c.CommonName.Contains(param.sSearch)
||
c.BotanicalName.Contains(param.sSearch)
||
c.PlantParts.Contains(param.sSearch)
||
c.Distillation.Contains(param.sSearch));
}
else
{
filteredOils = allOils;
}
var sortColumnIndex = Convert.ToInt32(Request["iSortCol_0"]);
Func<Oil, string> orderingFunction = (c => sortColumnIndex == 1 ? c.CommonName :
sortColumnIndex == 2 ? c.BotanicalName :
c.PlantParts);
var distillationFilter = Convert.ToString(Request["sSearch_4"]);
var commonFilter = Convert.ToString(Request["sSearch_1"]);
var botanicalFilter = Convert.ToString(Request["sSearch_2"]);
var plantFilter = Convert.ToString(Request["sSearch_3"]);
if (!string.IsNullOrEmpty(commonFilter))
{
filteredOils = filteredOils.Where(c => c.CommonName.Contains(commonFilter));
}
if (!string.IsNullOrEmpty(botanicalFilter))
{
filteredOils = filteredOils.Where(c => c.BotanicalName.Contains(botanicalFilter));
}
if (!string.IsNullOrEmpty(plantFilter))
{
filteredOils = filteredOils.Where(c => c.PlantParts.Contains(plantFilter));
}
if (!string.IsNullOrEmpty(distillationFilter))
{
filteredOils = filteredOils.Where(c => c.Distillation.Contains(distillationFilter));
}
var sortDirection = Request["sSortDir_0"];
if (sortDirection == "asc")
filteredOils = filteredOils.OrderBy(orderingFunction);
else
filteredOils = filteredOils.OrderByDescending(orderingFunction);
var displayedOils = filteredOils
.Skip(param.iDisplayStart)
.Take(param.iDisplayLength);
var result = from c in displayedOils
select new[] { Convert.ToString(c.OilId), c.CommonName, c.BotanicalName, c.PlantParts, c.Distillation };
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = allOils.Count(),
iTotalDisplayRecords = filteredOils.Count(),
aaData = result
},
JsonRequestBehavior.AllowGet);
}
P.s. The database has 5million row so please suggest with performance point of view too.
First of all you should not use _context.Oils.ToList(); then you will retrieve all your records from the database before filtering them. If you place the ToList() after .Take(param.iDisplayLength) all your selection code will be translated to a query in your database and only the relevant records will be retrieved. There is a difference between Contains executed by the Linq extension (case sensitve; see also this SO question) and the Contains that will we translated by the Entity Framework to a LIKE SQL statement (see this SO question and this video about tracing the SQL statements generated by your code).
As Shyju mentioned, refer to this post:
Case insensitive 'Contains(string)'
This will give you a wholistic idea of what to expect.
Here is a small excerpt from the post for your reference:
To test if the string paragraph contains the string word (thanks #QuarterMeister)
culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0
Where culture is the instance of CultureInfo describing the language that the text is written in.
In addition, I encourage you to visit this article which has an exhaustive comparison of various methods’ performances while checking if a string occurs within a string. This should help you decide what approach to take for better performance.
http://cc.davelozinski.com/c-sharp/fastest-way-to-check-if-a-string-occurs-within-a-string
Based on your question, you may have to create an extension method which will use different approach to preform check based on the type of input to achieve best performance.
Hope this helps!
I have a function that creates an instance of DataTables and for some reason it initializes n-times after destruction. I only noticed this because I add custom fields on init and they were multiplying. I could prevent that but that only deals with the symptom.
To clarify, after I "destroy: the instance and reinitialize it, to change the data source, if it's the second time it initializes twice. Three times if it's the 3rd time, etc.
I speculate that the table variable is part of the closure formed by the function because even if I set table = null the same thing happens.
How can I prevent this?
DataTables Function
/*Create a DataTable on tableElementID using pageUrl as the source*/
function ajaxLoadTable ( pageUrl, tableElementID ) {
window.table = $(tableElementID)
.on( 'init.dt', function () {
//The success function is used internally so it should NOT be overwritten, have to listen for this event instead
//Add our custom fields _length refers to an element generated datatables
if ( additionalElements.saveButton ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><button>Save All Edits</button></div>');
}
if ( additionalElements.selectState ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><label>Project State: <select name="projectState" style="width:auto;"><option>Select ...</option><option value="Active">Active</option><option value="Historical">Historical</option></select></label></div>');
}
if ( additionalElements.searchBox ) {
$(tableElementID + '_length').after('<div class="dataTables_filter"><label>Search:<input type="search" id="customSearch" style="width:auto;"></label></div>');
}
})
.DataTable({
"processing": true,
"serverSide": true,
"ajax":{
type: "POST",
url: pageUrl,
data: function ( additionalData ) {
$('.serverData').each( function( index, element ){
if( element.nodeName === "SELECT"){
additionalData[element.name.toUpperCase()] = element.options[element.selectedIndex].value;
return true; //return true is equivalent to continue for $.each
}
additionalData[element.name.toUpperCase()] = element.value;
});
},
dataType: "json"
},
"pageLength": 4,
"lengthMenu": [ 4, 8, 12, 16, 24 ],
"searchDelay": 1500,
"columnDefs":
{ "targets": 0,
"orderable": false,
"data": {
"_": "display"
}
}
});
}
Destruction Function
/*Load the selected project state*/
$('html').on( 'change' , '[name=projectState]' ,function(){
var currentState = $('option:selected', this).val();
$('#projectState').val(currentState);
//Remove the old table records and the datatables. Order matters, otherwise there is unsual behavior.
if( $.fn.DataTable.isDataTable( '#searchTable' ) ) {
window.table.destroy();
window.table = null;
}
$('.projectStateText').html( currentState );
//Get the new table records
ajaxLoadTable( *some undisclosed URL*, '#searchTable');
});
I have a datatable set up in an application where the rows contain input fields and I have sort and filter the tables based on the text values of the input fields. I managed to get sorting working correctly but I cannot for the life of me get search to work. I think the problem is related to the fact that the table is generated and populated dynamically by another JavaScript that runs prior to datatables being called on it.
Here is the JavaScript so far:
/* Create an array with the values of all the input boxes in a column */
$.fn.dataTable.ext.order["dom-text"] = function ( settings, col )
{
return this.api().column( col, {order:"index"} ).nodes().map( function ( td, i ) {
return $("input", td).val();
} );
}
/* Create an array with the values of all the input boxes in a column, parsed as numbers */
$.fn.dataTable.ext.order["dom-text-numeric"] = function ( settings, col )
{
return this.api().column( col, {order:"index"} ).nodes().map( function ( td, i ) {
return $("input", td).val() * 1;
} );
}
/* Create an array with the values of all the select options in a column */
$.fn.dataTable.ext.order["dom-select"] = function ( settings, col )
{
return this.api().column( col, {order:"index"} ).nodes().map( function ( td, i ) {
return $("select", td).val();
} );
}
$(document).ready(function() {
var table = $("#service_group0").DataTable({
"searching": true,
"ordering": true,
"columns": [
{ "orderDataType": "dom-text", type: \'html\' },
{ "orderDataType": "dom-select",type: \'html\' },
{ "orderDataType": "dom-text" , type: \'string\'},
{ "orderDataType": "dom-text", type: \'string\' },
{ "orderDataType": "dom-text", type: \'string\'},
{ "orderDataType": "dom-select" },
{ "orderDataType": "dom-select" },
{ "orderDataType": "dom-text-numeric"},
{ "orderDataType": "dom-text", type: \'date\'},
null,
{ "orderDataType": "dom-select" },
{ "orderDataType": "dom-text-numeric"},
{ "orderDataType": "dom-select" },
{ "orderDataType": "dom-text", type: \'string\' },
{ "orderDataType": "dom-text", type: \'string\' },
{ "orderDataType": "dom-text", type: \'date\'},
null,
null
],
initComplete: function () {
this.api().columns().every( function () {
var column = this;
if(column.index() == 5){
var select = $("<select id=\'strength_search\'></select>")
.appendTo( $(column.footer()).empty());
var strength_hidden = document.getElementById("strength_hidden").value;
select.append(strength_hidden);
}
else if(column.index() == 6){
var select = $("<select id=\'dose_search\'></select>")
.appendTo( $(column.footer()).empty());
var dose_hidden = document.getElementById("dose_hidden").value;
select.append(dose_hidden);
}
});
}
});
There is simply to much html to paste here so I have created a jsfiddle: http://jsfiddle.net/q715LncL/12/
If you go to the jsfiddle and type stuff into the empty text fields then go to the search box and try to filter based on something you typed in it always returns no results. How can I get it to filter on the changes made to the live inputs?
You need to add a custom filter function that checks the values of the input boxes, DataTables converts the HTML to strings and you can't recover the live value from them, so you would have to do something like this:
$.fn.dataTable.ext.search.push(
function( settings, data, dataIndex ) {
var searchTerm = $('.dataTables_filter input').val();
var $row = $('.dataTable tbody tr').eq(dataIndex);
if(!searchTerm) {
return true;
}
return $row.find('td input').filter(function() {
return ~$(this).val().toLowerCase().indexOf(searchTerm.toLowerCase())
}).size();
}
);
http://jsfiddle.net/q715LncL/14/
SOLUTION
You can use columnDefs to target a specific column using zero-based index in targets option and render to return selected value during searching (type === 'filter') or sorting (type === 'order').
columnDefs: [
{
targets: [0,1,2,3,4,5,6],
render: function(data, type, full, meta){
if(type === 'filter' || type === 'sort'){
var api = new $.fn.dataTable.Api(meta.settings);
var td = api.cell({row: meta.row, column: meta.col}).node();
data = $('select, input', td).val();
}
return data;
}
}
]
Also you need to invalidate cell data once data changes as shown below (according to this solution).
$('tbody select, tbody input', table.table().node()).on('change', function(){
table.row($(this).closest('tr')).invalidate();
});
DEMO
See this jsFiddle for code and demonstration.
NOTES
I have commented code that causes JavaScript errors because of the non-existing elements.