I am using handsontable with the select2 editor but I cannot seem to make dynamic options work with the dropdown menu, i.e. the options set at the the time of the handsontable initialisation are the only options that ever seem to show.
I have tried using a global variable as the source of the options and updating that at various points in my code and also using a function to return the same variable but neither attempt seems to work.
e.g.
var hot;
var data = [];
function customDropdownRenderer(instance, td, row, col, prop, value, cellProperties) {
if (instance.getCell(row, col)) {
$(instance.getCell(row,col)).addClass('select2dropdown');
}
var selectedId;
var colOptions = cellProperties.select2Options.data;
if (colOptions != undefined) {
for (var index = 0; index < colOptions.length; index++) {
if (parseInt(value) === colOptions[index].id) {
selectedId = colOptions[index].id;
value = colOptions[index].text;
}
}
Handsontable.TextCell.renderer.apply(this, arguments);
}
}
var requiredText = /([^\s])/;
$(document).ready(function(){
var
$container = $("#example1"),
$parent = $container.parent(),
autosaveNotification;
hot = new Handsontable($container[0], {
columnSorting: true,
stretchH: 'all',
startRows: 8,
startCols: 5,
rowHeaders: true,
colHeaders: ['Description', 'Cost', 'Remarks'],
columns: [
{ data: 'description' },
{
data: 'cost',
editor: 'select2',
renderer: customDropdownRenderer,
select2Options: { data: getData(), dropdownAutoWidth: true }
},
{ data: 'remarks' },
],
minSpareCols: 0,
minSpareRows: 1,
contextMenu: true,
data: []
});
data = [{id:'fixed',text:'Fixed'},{id:'variable',text:'Variable'}];
});
function getData() {
return data;
}
http://jsfiddle.net/zfmdu4wt/27/
You have defined data multiple times and it is causing contention.
The following changes will fix it:
Define the following immediately after the .ready() function:
var source = [{id:'fixed',text:'Fixed'},{id:'variable',text:'Variable'}];
and update the select2Options to the following:
select2Options : { data: source, dropdownAutoWidth: true }
I managed to get it working by re-using some code I had used to solve the same problem with the xeditable plugin.
Here's the updated code:
var hot;
var data = [];
function customDropdownRenderer(instance, td, row, col, prop, value, cellProperties) {
if (instance.getCell(row, col)) {
$(instance.getCell(row,col)).addClass('select2dropdown');
}
var selectedId;
var colOptions = cellProperties.select2Options.data;
if (colOptions != undefined) {
for (var index = 0; index < colOptions.length; index++) {
if (parseInt(value) === colOptions[index].id) {
selectedId = colOptions[index].id;
value = colOptions[index].text;
}
}
Handsontable.TextCell.renderer.apply(this, arguments);
}
}
var requiredText = /([^\s])/;
$(document).ready(function(){
var
$container = $("#example1"),
$parent = $container.parent(),
autosaveNotification;
hot = new Handsontable($container[0], {
columnSorting: true,
stretchH: 'all',
startRows: 8,
startCols: 5,
rowHeaders: true,
colHeaders: ['Description', 'Cost', 'Remarks'],
columns: [
{ data: 'description' },
{
data: 'cost',
editor: 'select2',
renderer: customDropdownRenderer,
// select2Options: { data: getData(), dropdownAutoWidth: true }
select2Options: { data: getSource(), dropdownAutoWidth: true, width: 'resolve', initSelection: getInitSel(false), query: getQuery }
},
{ data: 'remarks' },
],
minSpareCols: 0,
minSpareRows: 1,
contextMenu: true,
data: []
});
data = [{id:'fixed',text:'Fixed'},{id:'variable',text:'Variable'}];
});
/*
function getData() {
return data;
}
*/
// New Code
function getSource() {
return data;
};
function getQuery(options) {
options.callback({ results : getSource() });
};
function getInitSel(multiple) {
return function(el, cb) {
var t, toSet = [], sc = getSource();
el[0].value.split(',').forEach(function(a) {
for (var i = 0; i < sc.length; i++) {
if (sc[i].id == Number(a.trim())) {
t = sc[i];
}
}
// or, if you are using underscore.js
// t = _.findWhere(sc, { id : Number(a.trim()) });
if(t) toSet.push(t);
});
cb(multiple ? toSet : (toSet.length ? toSet[0] : null));
};
};
and a fiddle for demonstration - http://jsfiddle.net/zfmdu4wt/38/
Related
I am trying to double click, in onChange function, on the in progress grid to open a kendoWindow share on two grids on the same page but it does not open. When looking at the console, it gives error below relating to my inprogress grid, however, i can see data items in the actual grid. How should I go about to fix?
Uncaught TypeError: dataItem is undefined
Declaration
$(document).ready(function () {
getdata(que = 0);
$('#subform').hide(); $('#upd').hide();
getdata(que = 1);
});
GetData function
function getdata(que) {
//console.log('que is:',que);
if(que==0){
// console.log('your in queue 0');
var remoteDataSource = new kendo.data.DataSource({
transport: {
read: {
url: "triage_inv/get?id=0",
dataType: "json"
},
sort: { field: "open_dt_tm", dir: "asc"},
parameterMap: function (options, operation) {
if (operation !== "read" && options.models) {
return {
models: kendo.stringify(options.models)
};
}
}
},
pageSize: 20,
page: 1
});
}else if(que==1){
//console.log('your in queue 1');
var remoteDataSource1 = new kendo.data.DataSource({
transport: {
read: {
url: "triage_inv/get?id=1",
dataType: "json"
},
sort: { field: "open_dt_tm", dir: "asc"},
parameterMap: function (options, operation) {
if (operation !== "read" && options.models) {
return {
models: kendo.stringify(options.models)
};
}
}
},
pageSize: 20,
page: 1
});
}
InProgress grid object
$('#inprogress').kendoGrid({
dataSource: remoteDataSource,
height: 300,
editable: "popup",
change: onChange,
sortable: true,
resizable: true,
selectable: true,
toolbar: ["excel"],
excel: {
...
Backlog grid object
$('#backlog').kendoGrid({
dataSource: remoteDataSource1,
height: 400,
editable: "popup",
change: onChanger,
sortable: true,
resizable: true,
selectable: true,
toolbar: ["excel"],
excel: {
onChanger function
function onChanger(e) {
var rows1 = e.sender.select();
rows1.each(function (e) {
var grid1 = $("#backlog").data("kendoGrid");
grid1.tbody.find("tr").unbind().dblclick(function(e) {
var dataItem1 = grid1.dataItem(this);
//getsimilarscripts(searchData);
var sr_num1 = dataItem1.ticket_number;
myWindow.data("kendoWindow").open();
onChange function
function onChange(e) {
var rows = e.sender.select();
rows.each(function (e) {
var grid = $("#inprogress").data("kendoGrid");
// Double click to retrieve ticket detail
grid.tbody.find("tr").unbind().dblclick(function(e) {
console.log('inprogress grid >',grid.currentRow().dataItem(this));
var dataItem = grid.dataItem(this);
//getsimilarscripts(searchData);
var sr_num = dataItem.ticket_number;
myWindow.data("kendoWindow").open();
I removed the following if/else if conditions and it is working like a champ.
...
if(que==0){
...
}else if(que==1){
How can I get the first element of an array in javascript by a means other than array[0]?
I do not know why, but for some reason the array that gets passed into the method has its first, and only, element at index 5, not 0.
Edit - Here is my attempt at providing a minimal reproducible example.
// Initialize database connection string datatable editor
editor = new $.fn.dataTable.Editor({
table: "#remoteSqlDatabaseTable",
ajax: $("#UpdateIntegrationRemoteSqlDatabaseAction").val(),
fields: [
{ label: 'Reference Name', name: 'Name' },
{ label: 'IP Address or Host', name: 'IpAddress' },
{ label: 'Port', name: 'Port' },
{ label: 'Username', name: 'UserCredential' },
{ label: 'Password', name: 'PasswordCredential', def: '' },
{ name: 'button' }
]
});
editor
.on('open', function (e, type) {
if (type === 'inline') {
// Listen for a tab key event when inline editing
$(document).on('keydown.editor', function (e) {
if (e.keyCode === 9 || e.keyCode === 13) {
editor.blur();
}
});
}
if (editor.s.includeFields[0] === 'PasswordCredential') {
editor.field(editor.s.includeFields[0]).val('');
}
})
.on('close', function () {
$(document).off('keydown.editor');
})
.on('preSubmit', function (e, edit) {
if (edit.data[1].PasswordCredential !== undefined) {
var value = edit.data[1].PasswordCredential;
// check for null or whitespace
if (typeof value === 'undefined' || value == null || value.replace(/\s/g, '').length < 1) {
editor.close();
return false;
}
}
});
// Configure inline editing columns
$('#remoteSqlDatabaseTable').on('click', 'tbody td:not(:last-child)', function (e) {
editor.inline(this, {
submitOnBlur: true
});
});
// Reload data on edit to handle reordering
//editor.on("submitSuccess", function () {
// var table = $('#databaseConnectionTable').DataTable();
// console.log('submitSuccess');
// table.ajax.reload(function () {
// initTableStyling();
// });
//})
// Initialize datatable
var table = $('#remoteSqlDatabaseTable').DataTable({
paging: false,
searching: false,
info: false,
ordering: false,
ajax: $("#GetIntegrationRemoteSqlDatabasesAction").val(),
dom: 'Bfrtip',
columns: [
{ data: 'Name' },
{ data: 'IpAddress' },
{ data: 'Port' },
{ data: 'UserCredential' },
{ data: 'PasswordCredential' },
{ data: null }
],
select: false,
responsive: false,
buttons: [],
columnDefs: [
{
targets: -1,
data: null,
render: function (data, type, row, meta) {
return '<button class="btn red" type="button">Delete</button>';
}
}
],
initComplete: function (settings, json) {
//initTableStyling();
}
});
Like i said, i am not sure exactly where the problem is coming from. The method that is breaking is the editor.on('presubmit'), where it check if(edit.data[1] . edit.data[1] is the object that only has a 5th element.
this is everything relating to the table whos editing is the problem.
Updated answer, this works with objects. It isn't a pretty as find filter answers but it simply works.
Loop through the object and set a value.
Since there is only one element, it will set the variable without overwriting anything.
testArray = {"5" : "test"};
var val = "";
for(key in testArray){val = testArray[key];}
console.log(val);
You can do this:
let [first_element] = your_array
I have a Kendo Grid with checkbox selection in my MVC web application. I am trying to set some initial selections that trigger on databind. Here is my grid code:
#(Html.Kendo().Grid<MyProject.ViewModels.MyViewModel>()
.Name("MyGrid")
.Columns(columns => {
columns.Select().Width(50);
columns.Bound(c => c.Id);
columns.Bound(c => c.Name).Title("Name")
})
.Pageable()
.Sortable()
.Events(ev => ev.DataBound("onChange"))
.PersistSelection()
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(p => p.Id))
.Read(read => read.Action("GetData", "Test"))
))
You'll notice that under the events parameter I've set a function to trigger on DataBound called onChange. This function is where I want to make my initial selections. I started writing a function to achieve this and Telerik assisted with some code:
function onChange(e) {
//Sample array
var arr = [206, 210];
for (var i = 0; i < e.sender.items().length; i++) {
//206 is a test value, I want to pass an array in.
if (e.sender.dataItem(e.sender.items()[i]).Id == 206) {
e.sender.select(e.sender.items()[i]);
}
}
}
This code only takes me part of the way. What I want to do and where I need help is, adjusting this code to accept an array of Ids and select those items. For testing purposes, I have made a very basic array called arr but I'm not sure how to pass this into the loop.
I attempted to get it working using a jquery each loop to iterate over every value in the array and select the row but it didn't work. The code was something like:
function onChange(e) {
//Sample array
var arr = [206, 210];
$.each(arr, function(i, v) {
if (e.sender.dataItem(e.sender.items()[i]).Id == v) {
e.sender.select(e.sender.items()[i]);
}
})
}
Any help is appreciated.
Use indexOf() to check if each Id exists in the array:
function onChange(e) {
//Sample array
var arr = [206, 210],
grid = e.sender;
for (var i = 0; i < grid.items().length; i++) {
if (arr.indexOf(grid.dataItem(grid.items()[i]).Id) > -1) {
grid.select(grid.items()[i]);
}
}
}
Working example:
let arr = [1,3,5,7],
data = [1,2,3,4,5,6,7,8,9,10];
for (let i = 0, len = data.length; i < len; i++) {
if (arr.indexOf(data[i]) > -1) {
console.log(`item ${data[i]} exists in the array`);
}
}
Here I have a namespaced myApp with functions where I added a klookup function to return the first match in an array, then select using that. Note I used the dataBound event using a kendo data sample.
// create a namespace for my functions
var myApp = myApp || {};
myApp.funcs = {
klookup: function(myArray, searchTerm, property, firstOnly) {
var found = [];
var i = myArray.items().length;
while (i--) {
if (myArray.dataItem(myArray.items()[i])[property] == searchTerm) {
found.push(myArray.items()[i]);
if (firstOnly) break; //if only the first
}
}
return found;
},
onDataBound: function(e) {
// console.log("onDataBound");
myApp.data.Sender = e.sender;
let s = myApp.data.Sender
// console.dir(myApp.data.arr);
let rows = s.items();
//console.log(rows);
myApp.data.arr.forEach(function(entry) {
let found = myApp.funcs.klookup(s, entry, "OrderID", true);
s.select(found[0]);
});
}
};
// add data to my namespace
myApp.data = {
arr: [10248, 10250]
};
$(function() {
$("#grid").kendoGrid({
dataSource: {
type: "odata",
transport: {
read: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Orders"
},
schema: {
model: {
fields: {
OrderID: {
type: "number"
},
Freight: {
type: "number"
},
ShipName: {
type: "string"
},
OrderDate: {
type: "date"
},
ShipCity: {
type: "string"
}
}
}
},
pageSize: 5,
serverPaging: true,
serverFiltering: true,
serverSorting: true
},
persistSelection: true,
dataBound: myApp.funcs.onDataBound,
height: 550,
filterable: true,
sortable: true,
pageable: true,
columns: [{
selectable: true,
width: "50px"
}, {
field: "OrderID",
filterable: false
},
"Freight",
{
field: "OrderDate",
title: "Order Date",
format: "{0:MM/dd/yyyy}"
}, {
field: "ShipName",
title: "Ship Name"
}, {
field: "ShipCity",
title: "Ship City"
}
]
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.2.620/styles/kendo.common.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2018.2.620/styles/kendo.blueopal.min.css" />
<script src="https://kendo.cdn.telerik.com/2018.2.620/js/kendo.all.min.js"></script>
<div id="grid"></div>
I have something similar to the JSFiddle mentioned here. The JSFiddle works fine, however, I am seeing a strange behaviour in my code. The description of very first
cell is always empty even though I am seeing that in the returned data (in JSON format) from the webservice, the description is present for each record. Please
take a look at the code below and let me know if I am missing anything:
this.processEmployees = function (data_, textStatus_, jqXHR_) {
var collection = data_.employees;
// A helper function used for employee codes below.
var isUsedKey = function (arrayOfObject, key) {
for (var i = 0; i < arrayOfObject.length; i += 1) {
if (arrayOfObject[i].key == key) {
return true;
}
}
return false;
};
var employeeCodes = [];
for (var i = 0; i < collection.length; i++) {
if (i == 0) {
var newItem = {};
newItem.key = collection[i].code_value;
newItem.dates = [collection[i].code_assignment_date];
newItem.description = collection[i].code_description;
newItem.hiringCriterion = collection[i].hiring_criteria;
newItem.name = collection[i].name;
console.log("Would like to check code_description for first item:",collection[i].code_description);
//debugger;
employeeCodes.push(newItem);
} else {
var item = collection[i];
var itemName = item.code_value;
var itemDate = item.code_assignment_date;
var itemDescription = item.code_description;
var hiringCriterion = item.hiring_criteria;
var itemCodeName = item.name;
if (isUsedKey(employeeCodes, itemName)) {
for (var j = 0; j < employeeCodes.length; j++) {
if (employeeCodes[j].key == itemName) {
var index = employeeCodes[j].dates.length;
employeeCodes[j].dates[index] = itemDate;
}
}
} else {
var nextNewItem = {};
nextNewItem.key = itemName;
nextNewItem.dates = [itemDate];
nextNewItem.code_description = itemDescription;
nextNewItem.hiring_criteria = hiringCriterion;
nextNewItem.name = itemCodeName;
employeeCodes.push(nextNewItem);
}
}
}
var newSource = {
localdata: employeeCodes,
datafields: [{
name: 'code_value',
type: 'string',
map: 'key'
},
{
name: 'code_assignment_date',
type: 'date',
map: 'dates>0'
},
{
name: 'name',
type: 'string'
},
{
name: 'code_description',
type: 'string'
},
{
name: 'hiring_criteria',
type: 'string'
}
],
datatype: "array"
};
var newAdapter = new $_.jqx.dataAdapter(newSource);
var iconrenderer = function (row, columnfield, value, defaulthtml, columnproperties) {
var icon = '';
if (employeeCodes[row].dates.length > 1) {
icon = '<img src="images/icon-down.png" style="position: absolute; right: 5px;" />';
}
return '<span style="position: relative; width: 100%; margin: 4px; float: ' + columnproperties.cellsalign + ';">' + newAdapter.formatDate(value, 'd') + icon + '</span>';
};
$_(self.gridSelector).jqxGrid({
source: newAdapter,
editable: true,
width: '600',
pageable: true,
sortable: true,
autoheight: true,
theme: 'classic',
height: '170',
columnsResize: true,
columns: [
{
text: 'Employee Name',
datafield: 'name',
width: 85,
},
{
text: 'Code Value',
datafield: 'code_value',
width: 75,
editable: false
},
{
text: 'Latest Date(s)',
datafield: 'code_assignment_date',
cellsformat: 'd',
columntype: 'combobox',
width: 100
createeditor: function (row, column, editor) {
var info = $_(self.gridSelector).jqxGrid('getrowdata', row);
console.log("length of info: " + info);
var groupName = info.code_value;
console.log("Contents of groupName: " + groupName);
var dates = [];
for (var i = 0; i < employeeCodes.length; i++) {
if (employeeCodes[i].key == groupName) {
dates = employeeCodes[i].dates;
}
}
editor.jqxComboBox({ autoDropDownHeight: false, source: dates, promptText: "Previous Date(s):", scrollBarSize: 10 });
},
initeditor: function (row, column, editor) {
var info = $_(self.gridSelector).jqxGrid('getrowdata', row);
var groupName = info.code_value;
var dates = [];
for (var i = 0; i < employeeCodes.length; i++) {
if (employeeCodes[i].key == groupName) {
dates = employeeCodes[i].dates;
}
}
editor.jqxComboBox({
autoDropDownHeight: false,
source: dates,
promptText: "Dates:",
width: 100,
scrollBarSize: 10,
renderer: function (index_, label_, value_) {
return formatDateString(value_);
},
renderSelectedItem: function (index, item) {
var records = editor.jqxComboBox('getItems');
var currentRecord = records[index].label;
return formatDateString(currentRecord);;
}
});
},
cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
// return the old value, if the new value is empty.
if (newvalue == "") return oldvalue;
}
},
{
text: 'Description',
datafield: 'code_description'
},
{
text: 'Hiring Criterion',
datafield: 'hiring_criteria',
editable: false,
hidden: true
}
],
});
}; // End of processEmployees
For Example, if my JSON is the following:
{
"employees": [{
"code_value": "B1",
"code_assignment_date": "2016-12-13 23:04:00.0",
"code_type": 7,
"code_description": "This employee has received his salary",
"name": "Peter",
"hiring_criteria": null
}, {
"code_value": "A1",
"code_assignment_date": "2016-05-20 05:00:00.0",
"code_type": 7,
"code_description": "Employee has 5 vacation days left",
"name": "Jack",
"hiring_criteria": null
}],
"webServiceStatus": {
"status": "SUCCESS",
"message": "2 results"
}
}
I am not seeing the very first code_description value which is This employee has received his salary in the above JSON response whereas the following
line mentioned in the above code clearly shows it in the console panel:
console.log("Would like to check code_description for first item:",collection[i].code_description);
newItem.description = collection[i].code_description;
---Replace newItem.description to newItem.code_description in First object condition
newItem.hiringCriterion = collection[i].hiring_criteria;
---Replace newItem.hiringCriterion to newItem.hiring_criteria in First object condition
I have a jquery DataTable as
html page
<div id="content">
</div>
js code
(function ($) {
'use strict';
var module = {
addTable: function () {
var output = '<table id="table1"></table>';
$('#content').append('<p></p>' + output);
var data = [];
data = this.getData();
$('#table1').dataTable({
"data": data,
"columns": [
{
"title": 'Name',
mDataProp: 'name',
width: '20%'
},
{
"title": 'Company',
mDataProp: 'company'
},
{
"title": 'Salary',
mDataProp: 'salary'
}],
'scrollY': '400px',
'scrollCollapse': false,
'paging': false
});
},
getData: function () {
var arr = [];
for (var i = 0; i < 100; i++) {
var obj = {
name: 'John',
company: 'XYZ',
salary: '$XYZ'
};
arr.push(obj);
}
return arr;
}
};
$(document).ready(function () {
$('#content').append('Loading....');
module.addTable();
});
})(jQuery);
On initial load, it shows an empty table. Data comes after performing some search. How to show the data by default on initial load?
This is due to javascripts asynchronicity. getData() is not finished at the time of the dataTable initialization. You could make some refactoring, so getData invokes addTable as a callback instead.
var module = {
addTable: function (data) {
var output = '<table id="table1"></table>';
$('#content').append('<p></p>' + output);
$('#table1').dataTable({
"data": data,
"columns": [
{
"title": 'Name',
mDataProp: 'name',
width: '20%'
},
{
"title": 'Company',
mDataProp: 'company'
},
{
"title": 'Salary',
mDataProp: 'salary'
}],
'scrollY': '400px',
'scrollCollapse': false,
'paging': false
});
},
getData: function (callback) {
var arr = [];
for (var i = 0; i < 100; i++) {
var obj = {
name: 'John',
company: 'XYZ',
salary: '$XYZ'
};
arr.push(obj);
}
return callback(arr);
},
init : function() {
this.getData(this.addTable);
}
};
...
module.init();
init() calls getData(callback) with addTable as param, addTable have had the param data added.
demo -> http://jsfiddle.net/bLzaepok/
I assume your getData code is only per example, and you are using AJAX (or whatever) IRL. Call the callback in the callback :
getData: function (callback) {
$.ajax({
...
success : function(data) {
callback(data);
}
});
}