Cannot get Datatables editor to initialize - javascript

I am working on a page where an xlsx file is uploaded, parsed into an array of objects (each row is an object) and then populated into a Data Table. I want to be able to edit the data in the table before i click another button and send it to the database. Cannot get the editing to work though. I have tried loads of different ways that I found online of initializing the editor but none work for me.
If I get rid of the editor code, a normal Data Table works fine, but as of now I get the error:
Cannot read property Editor of undefuned
Here is the code and how I populate the Data Table
$(function() {
let session = '<?=json_encode($_SESSION)?>';
//console.log(session);
var editor = new $.fn.dataTabe.Editor({
table: '#datatable'
});
var table = $('#datatable').DataTable();
table
.order( [ 3, 'asc' ] ).page.len( 10 )
.draw();
});
let fileIn;
let resultArray;
let finalArray = new Array;
document.getElementById('getFile').addEventListener('click', () => {
fileIn = document.getElementById('my_file_input').files[0];
console.log(fileIn);
parseExcel(fileIn);
setTimeout(() => {
resultArray.forEach((arr) => {
let company = (arr.company) ? arr.company : null;
let category = (arr.category) ? arr.category : null;
let pcs_no = (arr.pcs_no) ? arr.pcs_no : null;
let product_name = (arr.product_name) ? arr.product_name : null;
let min_range_rate = (arr.min_range_rate) ? arr.min_range_rate : null;
let max_range_rate = (arr.max_range_rate) ? arr.max_range_rate : null;
let max_total_rate_per_ha = (arr.max_total_rate_per_ha) ? arr.max_total_rate_per_ha : null;
let measure_unit = (arr.measure_unit) ? arr.measure_unit : null;
let range_notes = (arr.range_notes) ? arr.range_notes : null;
let comment = (arr.comment) ? arr.comment : null;
let crops;
let pack_size;
let chemical;
if(arr.crops) {
if(arr.crops.includes(',')) {
crops = arr.crops.split(',');
} else {
crops = arr.crops;
}
} else {
crops = null;
}
if(arr.pack_size) {
if(arr.pack_size.includes(',')) {
pack_size = arr.pack_size.split(',');
} else {
pack_size = arr.pack_size;
}
} else {
pack_size = null;
}
if(arr.chemical) {
if(arr.chemical.includes(',')) {
chemical = arr.chemical.split(',');
} else {
chemical = arr.chemical;
}
} else {
chemical = null;
}
let data = {
company,
category,
pcs_no,
product_name,
min_range_rate,
max_range_rate,
max_total_rate_per_ha,
measure_unit,
range_notes,
comment,
crops,
pack_size,
chemical
}
finalArray.push(data);
});
finalArray.forEach((arr) => {
table.row.add([
arr.company,
arr.category,
arr.category,
arr.pcs_no,
arr.product_name,
arr.pack_size,
arr.chemical,
arr.min_range_rate,
arr.max_range_rate,
arr.max_total_rate_per_ha,
arr.measure_unit,
arr.range_notes,
arr.crops,
(arr.comment) ? arr.comment.trim() : null
]).draw(false)
});
console.log(finalArray);
}, 3000);
});

Related

People.People.searchDirectoryPeople() query with multiple email addresses

In a Google Apps Script I need to query the Google user profile picture URL of many coworkers.
Here is a working example for a single user:
searchDirectoryPeople('jimmy.neutron#example.com');
function searchDirectoryPeople(query) {
const options = {
query: query,
readMask: 'photos,emailAddresses',
sources: ['DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE']
}
const people = People.People.searchDirectoryPeople(options);
if(people && people.people) {
Logger.log('size: '+people.people.length);
people.people.forEach(person => {
let url = '';
let email = '';
if(person) {
if(person.photos && person.photos[0]) {
url = person.photos[0].url;
}
if(person.emailAddresses && person.emailAddresses.length) {
person.emailAddresses.forEach(item => {
if(item.metadata && item.metadata.sourcePrimary) {
email = item.value;
}
});
}
}
Logger.log('email: '+email+': '+url);
//Logger.log('person: %s', JSON.stringify(person, null, 2));
});
} else {
Logger.log('no people.people');
}
}
I found out that I can query all jimmy people:
searchDirectoryPeople('jimmy');
I have the email address of all employees. I could loop through a big list of 1000+ employees one by one, but this is not practical. I am looking for a way to query multiple email addresses. The docs at https://developers.google.com/people/api/rest/v1/people/searchDirectoryPeople are cryptic for the query. I tried many things like these but nothing works:
'jimmy.neutron#example.com, carl.wheezer#example.com, cindy.vortex#example.com'
'jimmy.neutron#example.com OR carl.wheezer#example.com OR cindy.vortex#example.com'
I am looking for a query by list of email addresses as input, such as:
[ 'jimmy.neutron#example.com', 'carl.wheezer#example.com', 'cindy.vortex#example.com' ]
Is it possible to have an OR query in People.People.searchDirectoryPeople()?
UPDATE 2022-05-31
I tried looping through all emails and ran either into a quota limit or a script runtime limit.
#Lorena Gomez's answer is correct: First use the People.People.listDirectoryPeople() to get the resource names of all email address, followed by People.People.getBatchGet() to get the profile picture URL by resource names. The former limits to 1000 employees per call, the latter limits to 200. This works in our case where we have 1k+ email addresses as input, and 20k+ employees returned by listDirectoryPeople().
Working code:
const emails = [
'jimmy.neutron#example.com',
'carl.wheezer#example.com',
'cindy.vortex#example.com'
];
let emailToUrl = getGoogleProfilePictureUrls(emails);
Logger.log('emailToUrl: %s', JSON.stringify(emailToUrl, null, 2));
// expected output:
// emailToUrl: {
// "jimmy.neutron#example.com": "https://lh3.googleusercontent.com/a-/xxxx=s100",
// "carl.wheezer#example.com": "https://lh3.googleusercontent.com/a-/xxxx=s100",
// "cindy.vortex#example.com": "https://lh3.googleusercontent.com/a-/xxxx=s100"
// }
function getGoogleProfilePictureUrls(emails) {
let options = {
readMask: 'emailAddresses',
sources: ['DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE'],
pageSize: 1000
}
let run = 1;
let resourceNameToEmails = {};
let result = {};
while(run === 1 || result.nextPageToken) {
if(result.nextPageToken) {
options.pageToken = result.nextPageToken;
}
result = People.People.listDirectoryPeople(options);
Logger.log('request #' + (run++) + ', got '+result.people.length+' resource names');
result.people.forEach(person => {
if(person.emailAddresses) {
person.emailAddresses.forEach(obj => {
if(obj.metadata && obj.metadata.sourcePrimary) {
let email = obj.value
if(emails.indexOf(email) >= 0) {
resourceNameToEmails[person.resourceName] = email;
}
}
});
}
});
Utilities.sleep(200);
}
run = 1;
let emailToUrl = {};
let resourceNames = Object.keys(resourceNameToEmails);
let resourceNameBatch = resourceNames.splice(0, 200);
while(resourceNameBatch.length) {
options = {
personFields: 'photos',
resourceNames: resourceNameBatch,
sources: [ 'READ_SOURCE_TYPE_PROFILE' ]
};
result = People.People.getBatchGet(options);
if(result && result.responses) {
Logger.log('request #' + (run++) + ', got '+result.responses.length+' urls');
result.responses.forEach(person => {
let primaryUrl = '';
let url = '';
if(person.person && person.person.photos) {
person.person.photos.forEach(photo => {
if(photo.metadata && photo.metadata.source && photo.metadata) {
url = photo.url;
if(photo.metadata.source.type === 'PROFILE' && photo.metadata.primary) {
primaryUrl = url;
}
}
});
}
let email = resourceNameToEmails[person.person.resourceName];
emailToUrl[email] = primaryUrl || url;
});
}
Utilities.sleep(200);
resourceNameBatch = resourceNames.splice(0, 200);
}
return emailToUrl;
}
It looks like with Method: people.searchDirectoryPeople you can only specify one person at a time.
Another option could be People.People.getBatchGet() which will require an extra step but provides you information about a list of the people you specify. The request would look something like this:
const options = {
personFields: 'photos,emailAddresses',
resourceNames: [
'people/account_id',
'people/account_id',
'people/account_id'
],
sources: [
'READ_SOURCE_TYPE_PROFILE'
]
}
const people = People.People.getBatchGet(options);
You can get the user's account_id with Method: people.listDirectoryPeople
How about this?
function searchDirectoryPeople(query) {
const options = {
query: query,
readMask: 'photos,emailAddresses',
sources: ['DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE']
}
const people = People.People.searchDirectoryPeople(options);
if(people && people.people) {
Logger.log('size: '+people.people.length);
people.people.forEach(person => {
let url = '';
let email = '';
if(person) {
if(person.photos && person.photos[0]) {
url = person.photos[0].url;
}
if(person.emailAddresses && person.emailAddresses.length) {
person.emailAddresses.forEach(item => {
if(item.metadata && item.metadata.sourcePrimary) {
email = item.value;
}
});
}
}
return {"imgurl":url,"email":email}
});
}
}
function searchPlus(emailArray) {
let oA = [];
emailArray.forEach(e => {
oA.push(searchDirectoryPeople(e))
});
if(oA && oA.length) {
return oA;
}
}

Pass in structure correctly (js)

Face some problem in passing data into structure correctly
This is the structure
And this is how i pass in
var dates = require('dates');
module.exports.function = function getStart() {
var optionList = [
{option : "Latest headlines"},
{option : "Latest news"},
{option : "Top headlines"},
{option : "Top news"}
]
var currentTimeHour = dates.ZonedDateTime.getHour
var timePeriod = "";
if (currentTimeHour == 0 && currentTimeHour <= 12) {
timePeriod = "M"//Morning
} else if (currentTimeHour >= 13 && currentTimeHour <= 20) {
timePeriod = "A"//Afternoon
} else if (currentTimeHour <=23){
timePeriod = "N"//Night
}else {
timePeriod = null
}
var menu = {};
optionList.option.forEach(function(value,index,array){
menu[index] = {
whatuserwant : optionList[index],
timePeriod : timePeriod
}
});
return menu
}
And error pop out
Where did i gone wrong ,
And for extra question, is my if-else condition for currentTimeHour wrote correctly?
Regards.
forEach is defined on arrays where as optionList.option is not an array.
Try using only optionList
optionList.forEach(function(value,index,array){
menu[index] = {
whatuserwant : value.option,
timePeriod : timePeriod
}
});

How pagination works which is fetch from api in Nativescript

I am not getting the concepts of how pagination works in load more requested data from json api. Even i am getting the data of first page but for remaining pages not getting. If I am using the two api for paging and fetching data but still not getting. Any one help me clear this concepts please..
main-view-model.js
const httpModule = require("tns-core-modules/http");
var observableModule = require("tns-core-modules/data/observable");
function RegisterViewModel() {
var viewModel = observableModule.fromObject({
_sourceDataItems: [],
initDataItems: function () {
//This url for the pagination
var url1 = "https://example.org/api.php?action=query&gcmtitle=Category:xyz&pilimit=max&prop=pageimages&pithumbsize=200&generator=categorymembers&format=json&gcmcontinue=";
httpModule.getJSON(url1).then((r) => {
id = r.continue.gcmcontinue
gcm.push(id)
console.log(gcm)
var pageid = [];
for (var id in r.query.pages) {
pageid.push(id);
}
for (var i = 0; i < pageid.length; i++) {
if (r.query.pages[pageid[i]].thumbnail == null) {
let abc = {
ttt: r.query.pages[pageid[i]].title,
path1: "~/images/xyz.png"
}
this._sourceDataItems.push(abc)
}
else {
let aaa = {
ttt: r.query.pages[pageid[i]].title,
path1: r.query.pages[pageid[i]].thumbnail.source
}
this._sourceDataItems.push(aaa)
}
}
}, (e) => {
});
var gcm = [];
//This url for the fetching the data on scroll
for (var i = 0; i < gcm.length; i++) {
var url2 = "https://example.org/api.php?action=query&gcmtitle=Category:xyz&pilimit=max&prop=pageimages&pithumbsize=200&generator=categorymembers&format=json&gcmcontinue=" + gcm[i];
httpModule.getJSON(url2).then((r) => {
id= r.continue.gcmcontinue)
gcm.push(id)
console.log(r.continue.gcmcontinue)
var pageid = [];
for (var id in r.query.pages) {
pageid.push(id);
}
for (var i = 0; i < pageid.length; i++) {
//placeholder Image
if (r.query.pages[pageid[i]].thumbnail == null) {
let abc = {
ttt: r.query.pages[pageid[i]].title,
path1: "~/images/xyz.png"
}
this._sourceDataItems.push(abc)
}
else {
let aaa = {
ttt: r.query.pages[pageid[i]].title,
path1: r.query.pages[pageid[i]].thumbnail.source
}
this._sourceDataItems.push(aaa)
}
}
}, (e) => {
});
}
},
_DataItems: [],
addMoreItemsFromSource: function (chunkSize) {
let newItems = this._sourceDataItems.splice(0, chunkSize);
this._DataItems = this._DataItems.concat(newItems);
},
onLoadMoreItemsRequested: function (args) {
console.log("---load item called----");
const that = new WeakRef(this);
const listView = args.object;
if (this._sourceDataItems.length > 0) {
setTimeout(function () {
that.get().addMoreItemsFromSource(5);
listView.notifyLoadOnDemandFinished();
}, 100);
args.returnValue = true;
} else {
args.returnValue = false;
}
},
});
return viewModel;
}
exports.RegisterViewModel = RegisterViewModel;
Even loading of the data is working fine for page one. While passing the gcm continue id it wont take that please help me to clear this concept

How can I search this object to see if a user has a permission?

Given that I have:
var permissions = {
'blog': {
'article' : ['add_article', 'view_articles']
},
'gallery': {
'images' : ['upload_image', 'view_other_user_images']
}
}
var permissionsChecker = function(permissions) {
this.permissions = permissions;
this.hasPermissions = function (permissionsString) {
// permissionsString is in this.permissions
// return true
}
}
How can I search in the array within that object to check that I have a certain permission? I want to be able to search by giving a string like so:
'blog.article.add_article' or 'gallery.images.view_other_user_images'
I may not be understanding correctly, but I think this is what you're looking for:
var permissionsChecker = function(permissions) {
this.permissions = permissions;
this.hasPermissions = function (permissionsString) {
var [section, subsection, permission] = permissionsString.split('.');
if(this.permissions[section]){
if(this.permissions[section][subsection]){
return this.permissions[section][subsection].indexOf(permission) > -1;
}
}
return false;
};
}
var checker = new permissionsChecker(permissions);
checker.hasPermission('add_article');//true
checker.hasPermission('destroy_world');//false

Perfomance issues with large number of elements in Mithril.js

My app has a sort- and filterable list and a few inputs and checkboxes so far.
The problem appears if the list has more than 500 items, then every every element with user input (checkboxes, input fields, menus) start to have a lag around half a second increasing with the number of items in the list. The sorting and filtering of the list is done fast enough but the lag on the input elements is too long.
The question is: how can the list and the input elements be decoupled?
Here is the list code:
var list = {}
list.controller = function(args) {
var model = args.model;
var vm = args.vm;
var vmc = args.vmc;
var appCtrl = args.appCtrl;
this.items = vm.filteredList;
this.onContextMenu = vmc.onContextMenu;
this.isSelected = function(guid) {
return utils.getState(vm.listState, guid, "isSelected");
}
this.setSelected = function(guid) {
utils.setState(vm.listState, guid, "isSelected", true);
}
this.toggleSelected = function(guid) {
utils.toggleState(vm.listState, guid, "isSelected");
}
this.selectAll = function() {
utils.setStateBatch(vm.listState, "GUID", "isSelected", true, this.items());
}.bind(this);
this.deselectAll = function() {
utils.setStateBatch(vm.listState, "GUID", "isSelected", false, this.items());
}.bind(this);
this.invertSelection = function() {
utils.toggleStateBatch(vm.listState, "GUID", "isSelected", this.items());
}.bind(this);
this.id = "201505062224";
this.contextMenuId = "201505062225";
this.initRow = function(item, idx) {
if (item.online) {
return {
id : item.guid,
filePath : (item.FilePath + item.FileName).replace(/\\/g, "\\\\"),
class : idx % 2 !== 0 ? "online odd" : "online even",
}
} else {
return {
class : idx % 2 !== 0 ? "odd" : "even"
}
}
};
// sort helper function
this.sorts = function(list) {
return {
onclick : function(e) {
var prop = e.target.getAttribute("data-sort-by")
//console.log("100")
if (prop) {
var first = list[0]
if(prop === "selection") {
list.sort(function(a, b) {
return this.isSelected(b.GUID) - this.isSelected(a.GUID)
}.bind(this));
} else {
list.sort(function(a, b) {
return a[prop] > b[prop] ? 1 : a[prop] < b[prop] ? -1 : 0
})
}
if (first === list[0])
list.reverse()
}
}.bind(this)
}
};
// text inside the table can be selected with the mouse and will be stored for
// later retrieval
this.getSelected = function() {
//console.log(utils.getSelText());
vmc.lastSelectedText(utils.getSelText());
};
};
list.view = function(ctrl) {
var contextMenuSelection = m("div", {
id : ctrl.contextMenuId,
class : "hide"
}, [
m(".menu-item.allow-hover", {
onclick : ctrl.selectAll
}, "Select all"),
m(".menu-item.allow-hover", {
onclick : ctrl.deselectAll
}, "Deselect all"),
m(".menu-item.allow-hover", {
onclick : ctrl.invertSelection
}, "Invert selection") ]);
var table = m("table", ctrl.sorts(ctrl.items()), [
m("tr", [
m("th[data-sort-by=selection]", {
oncontextmenu : ctrl.onContextMenu(ctrl.contextMenuId, "context-menu context-menu-bkg", "hide" )
}, "S"),
m("th[data-sort-by=FileName]", "Name"),
m("th[data-sort-by=FileSize]", "Size"),
m("th[data-sort-by=FilePath]", "Path"),
m("th[data-sort-by=MediumName]", "Media") ]),
ctrl.items().map(function(item, idx) {
return m("tr", ctrl.initRow(item, idx), {
key : item.GUID
},
[ m("td", [m("input[type=checkbox]", {
id : item.GUID,
checked : ctrl.isSelected(item.GUID),
onclick : function(e) {ctrl.toggleSelected(this.id);}
}) ]),
m("td", {
onmouseup: function(e) {ctrl.getSelected();}
}, item.FileName),
m("td", utils.numberWithDots(item.FileSize)),
m("td", item.FilePath),
m("td", item.MediumName) ])
}) ])
return m("div", [contextMenuSelection, table])
}
And this is how the list and all other components are initialized from the apps main view:
// the main view which assembles all components
var mainCompView = function(ctrl, args) {
// TODO do we really need him there?
// add the main controller for this page to the arguments for all
// added components
var myArgs = args;
myArgs.appCtrl = ctrl;
// create all needed components
var filterComp = m.component(filter, myArgs);
var part_filter = m(".row", [ m(".col-md-2", [ filterComp ]) ]);
var listComp = m.component(list, myArgs);
var part_list = m(".col-md-10", [ listComp ]);
var optionsComp = m.component(options, myArgs);
var part_options = m(".col-md-10", [ optionsComp ]);
var menuComp = m.component(menu, myArgs);
var part_menu = m(".menu-0", [ menuComp ]);
var outputComp = m.component(output, myArgs);
var part_output = m(".col-md-10", [ outputComp ]);
var part1 = m("[id='1']", {
class : 'optionsContainer'
}, "", [ part_options ]);
var part2 = m("[id='2']", {
class : 'menuContainer'
}, "", [ part_menu ]);
var part3 = m("[id='3']", {
class : 'commandContainer'
}, "", [ part_filter ]);
var part4 = m("[id='4']", {
class : 'outputContainer'
}, "", [ part_output ]);
var part5 = m("[id='5']", {
class : 'listContainer'
}, "", [ part_list ]);
return [ part1, part2, part3, part4, part5 ];
}
// run
m.mount(document.body, m.component({
controller : MainCompCtrl,
view : mainCompView
}, {
model : modelMain,
vm : modelMain.getVM(),
vmc : viewModelCommon
}));
I started to workaround the problem by adding m.redraw.strategy("none") and m.startComputation/endComputation to click events and this solves the problem but is this the right solution? As an example, if I use a Mithril component from a 3rd party together with my list component, how should I do this for the foreign component without changing its code?
On the other side, could my list component use something like the 'retain' flag? So the list doesn't redraw by default unless it's told to do? But also the problem with a 3rd party component would persist.
I know there are other strategies to solve this problem like pagination for the list but I would like to know what are best practices from the Mithril side.
Thanks in advance,
Stefan
Thanks to the comment from Barney I found a solution: Occlusion culling. The original example can be found here http://jsfiddle.net/7JNUy/1/ .
I adapted the code for my needs, especially there was the need to throttle the scroll events fired so the number of redraws are good enough for smooth scrolling. Look at the function obj.onScroll.
var list = {}
list.controller = function(args) {
var obj = {};
var model = args.model;
var vm = args.vm;
var vmc = args.vmc;
var appCtrl = args.appCtrl;
obj.vm = vm;
obj.items = vm.filteredList;
obj.onContextMenu = vmc.onContextMenu;
obj.isSelected = function(guid) {
return utils.getState(vm.listState, guid, "isSelected");
}
obj.setSelected = function(guid) {
utils.setState(vm.listState, guid, "isSelected", true);
}
obj.toggleSelected = function(guid) {
utils.toggleState(vm.listState, guid, "isSelected");
m.redraw.strategy("none");
}
obj.selectAll = function() {
utils.setStateBatch(vm.listState, "GUID", "isSelected", true, obj.items());
};
obj.deselectAll = function() {
utils.setStateBatch(vm.listState, "GUID", "isSelected", false, obj.items());
};
obj.invertSelection = function() {
utils.toggleStateBatch(vm.listState, "GUID", "isSelected", obj.items());
};
obj.id = "201505062224";
obj.contextMenuId = "201505062225";
obj.initRow = function(item, idx) {
if (item.online) {
return {
id : item.GUID,
filePath : (item.FilePath + item.FileName).replace(/\\/g, "\\\\"),
class : idx % 2 !== 0 ? "online odd" : "online even",
onclick: console.log(item.GUID)
}
} else {
return {
id : item.GUID,
// class : idx % 2 !== 0 ? "odd" : "even",
onclick: function(e) { obj.selectRow(e, this, item.GUID);
m.redraw.strategy("none");
e.stopPropagation();
}
}
}
};
// sort helper function
obj.sorts = function(list) {
return {
onclick : function(e) {
var prop = e.target.getAttribute("data-sort-by")
// console.log("100")
if (prop) {
var first = list[0]
if(prop === "selection") {
list.sort(function(a, b) {
return obj.isSelected(b.GUID) - obj.isSelected(a.GUID)
});
} else {
list.sort(function(a, b) {
return a[prop] > b[prop] ? 1 : a[prop] < b[prop] ? -1 : 0
})
}
if (first === list[0])
list.reverse()
} else {
e.stopPropagation();
m.redraw.strategy("none");
}
}
}
};
// text inside the table can be selected with the mouse and will be stored
// for
// later retrieval
obj.getSelected = function(e) {
// console.log("getSelected");
var sel = utils.getSelText();
if(sel.length != 0) {
vmc.lastSelectedText(utils.getSelText());
e.stopPropagation();
// console.log("1000");
}
m.redraw.strategy("none");
// console.log("1001");
};
var selectedRow, selectedId;
var eventHandlerAdded = false;
// Row callback; reset the previously selected row and select the new one
obj.selectRow = function (e, row, id) {
console.log("selectRow " + id);
unSelectRow();
selectedRow = row;
selectedId = id;
selectedRow.style.background = "#FDFF47";
if(!eventHandlerAdded) {
console.log("eventListener added");
document.addEventListener("click", keyHandler, false);
document.addEventListener("keypress", keyHandler, false);
eventHandlerAdded = true;
}
};
var unSelectRow = function () {
if (selectedRow !== undefined) {
selectedRow.removeAttribute("style");
selectedRow = undefined;
selectedId = undefined;
}
};
var keyHandler = function(e) {
var num = parseInt(utils.getKeyChar(e), 10);
if(constants.RATING_NUMS.indexOf(num) != -1) {
console.log("number typed: " + num);
// TODO replace with the real table name and the real column name
// $___{<request>res:/tables/catalogItem</request>}
model.newValue("item_update_values", selectedId, {"Rating": num});
m.redraw.strategy("diff");
m.redraw();
} else if((e.keyCode && (e.keyCode === constants.ESCAPE_KEY))
|| e.type === "click") {
console.log("eventListener removed");
document.removeEventListener("click", keyHandler, false);
document.removeEventListener("keypress", keyHandler, false);
eventHandlerAdded = false;
unSelectRow();
}
};
// window seizes for adjusting lists, tables etc
vm.state = {
pageY : 0,
pageHeight : 400
};
vm.scrollWatchUpdateStateId = null;
obj.onScroll = function() {
return function(e) {
console.log("scroll event found");
vm.state.pageY = e.target.scrollTop;
m.redraw.strategy("none");
if (!vm.scrollWatchUpdateStateId) {
vm.scrollWatchUpdateStateId = setTimeout(function() {
// update pages
m.redraw();
vm.scrollWatchUpdateStateId = null;
}, 50);
}
}
};
// clean up on unload
obj.onunload = function() {
delete vm.state;
delete vm.scrollWatchUpdateStateId;
};
return obj;
};
list.view = function(ctrl) {
var pageY = ctrl.vm.state.pageY;
var pageHeight = ctrl.vm.state.pageHeight;
var begin = pageY / 41 | 0
// Add 2 so that the top and bottom of the page are filled with
// next/prev item, not just whitespace if item not in full view
var end = begin + (pageHeight / 41 | 0 + 2)
var offset = pageY % 41
var heightCalc = ctrl.items().length * 41;
var contextMenuSelection = m("div", {
id : ctrl.contextMenuId,
class : "hide"
}, [
m(".menu-item.allow-hover", {
onclick : ctrl.selectAll
}, "Select all"),
m(".menu-item.allow-hover", {
onclick : ctrl.deselectAll
}, "Deselect all"),
m(".menu-item.allow-hover", {
onclick : ctrl.invertSelection
}, "Invert selection") ]);
var header = m("table.listHeader", ctrl.sorts(ctrl.items()), m("tr", [
m("th.select_col[data-sort-by=selection]", {
oncontextmenu : ctrl.onContextMenu(ctrl.contextMenuId, "context-menu context-menu-bkg", "hide" )
}, "S"),
m("th.name_col[data-sort-by=FileName]", "Name"),
${ <request>
# add other column headers as configured
<identifier>active:jsPreprocess</identifier>
<argument name="id">list:table01:header</argument>
</request>
} ]), contextMenuSelection);
var table = m("table", ctrl.items().slice(begin, end).map(function(item, idx) {
return m("tr", ctrl.initRow(item, idx), {
key : item.GUID
},
[ m("td.select_col", [m("input[type=checkbox]", {
id : item.GUID,
checked : ctrl.isSelected(item.GUID),
onclick : function(e) {ctrl.toggleSelected(this.id);}
}) ]),
m("td.nameT_col", {
onmouseup: function(e) {ctrl.getSelected(e);}
}, item.FileName),
${ <request>
# add other columns as configured
<identifier>active:jsPreprocess</identifier>
<argument name="id">list:table01:row</argument>
</request>
} ])
}) );
var table_container = m("div[id=l04]",
{style: {position: "relative", top: pageY + "px"}}, table);
var scrollable = m("div[id=l03]",
{style: {height: heightCalc + "px", position: "relative",
top: -offset + "px"}}, table_container);
var scrollable_container = m("div.scrollableContainer[id=l02]",
{onscroll: ctrl.onScroll()}, scrollable );
var list = m("div[id=l01]", [header, scrollable_container]);
return list;
}
Thanks for the comments!
There are some good examples of when to change redraw strategy in the docs: http://mithril.js.org/mithril.redraw.html#changing-redraw-strategy
But in general, changing redraw strategy is rarely used if the application state is stored somewhere so Mithril can access and calculate the diff without touching DOM. It seems like your data is elsewhere, so could it be that your sorts method is getting expensive to run after a certain size?
You could sort the list only after events that modifies it. Otherwise it will be sorted on every redraw Mithril does, which can be quite often.
m.start/endComputation is useful for 3rd party code, especially if it operates on DOM. If the library stores some state, you should use that for the application state as well, so there aren't any redundant and possibly mismatching data.

Categories