I am using a JQWidget grid with paging to display table data, and I am replacing the values in one column with a string. This works fine for the initial page, but when I open the next page in the table I no longer get the string replacements, only the original value.
My home page uses this code, which works as expected ('A' and 'W' are replaced by 'newString' and 'newString2' in the table):
$("#jqxgrid").bind("bindingcomplete", function (event) {
var numrows = $("#jqxgrid").jqxGrid('getrows');
for (i = 0; i < numrows.length; i++) {
var value = $("#jqxgrid").jqxGrid('getcellvalue', i, 'column');
if (value == 'W') {
$("#jqxgrid").jqxGrid('setcellvalue', i, 'column', 'newString');
}
else if (value == 'A') {
$("#jqxgrid").jqxGrid('setcellvalue', i, 'column', 'newString2');
}
});
I tried a few ideas for the new page, such as placing the above binding function into a loop based on the number of pages:
var paginginfo = $("#jqxgrid").jqxGrid('getpaginginformation');
for (i = 0; i < paginginfo.pagescount; i++) { ...
and I also tried putting the binding function inside another function tied to the page change event:
$("#jqxgrid").bind("pagechanged", function (event) {
$("#jqxgrid").bind("bindingcomplete", function (event) { ...
but neither of these worked.
Perhaps 'numrows' is limiting setcellvalue to the first page?
Thanks //
The "binding function" is not a function, it is an event which is raised usually once the binding is completed, to in general you tried to bind to an event within another event and that normally results in nothing. You loop code - for (i = 0; i < numrows.length; i++) {... would probably not work in case of virtual paging, because the loop should be from the start index to the end index and the start index wouldn't be 0 on the second page.
I found this workaround..I am not sure if this is really foolproof, but it seems to work so far..if you know a more precise solution please post...
$("#jqxgrid").bind("bindingcomplete", function (event) {
var paginginfo = $("#jqxgrid").jqxGrid('getpaginginformation');
var pagenum = paginginfo.pagenum;
var pagesize = paginginfo.pagesize;
var pageRows = (pagenum + 1) * pagesize;
for (var i = 0; i < pageRows; i++) {
var value = $("#jqxgrid").jqxGrid('getcellvalue', i, 'currTrafDir');
if (value == 'W') { ......
Related
I'm working in Google apps script and seem to have screwed up one of my for loops. I'm sure that I am missing something trivial here, but I can't seem to spot it.
Code Snippet:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var lastRow = sheets[3].getLastRow();
var zw = sheets[3].getRange(2, 1, lastRow - 1, 26).getValues();
for (var j = 0; j < zw.length; ++j) {
if (zw[j][9] === 'Yes') {
var masterEmail = [];
var firstLetterLastName = [];
var first2Letter = [];
var masterEmail.push(zw[j][22]);
var firstLetterLastName.push(zw[j][1].charAt(0).toLowerCase());
var first2Letter.push(zw[j][1].charAt(0).toLowerCase() + zw[j][1].charAt(1).toLowerCase());
//The rest of the function follows...
}
}
What's Not Working:
The for loop doesn't increment. When running the code in a debugger, var j stays at a value of 0.0, and the rest of the function only runs based of off the values in the 0 position of zw.
What I need it to do (AKA - How I thought I had written it:)
The ZW variable is holding a 2 dimensional array of cell values from a Google sheet. I'm looping through that, checking the 9th value of each array entry for a string of "Yes" and then running the rest of the function (for each column with a "Yes") if the condition is true.
I thought I had this working before, but recently had to restructure and optimize some things. Now I'm starting to think I may need to rethink things and use a different loop method. Can anyone educate me?
Edit: Here's a bit more context as requested:
function menuItem1() {
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Are you sure you want to send emails?', ui.ButtonSet.YES_NO);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var lastRow = sheets[3].getLastRow();
var zw = sheets[3].getRange(2, 1, lastRow - 1, 26).getValues();
if (response === ui.Button.YES) {
for (var j = 0; j < zw.length; j++) {
if (zw[j][9] === 'Yes') {
var firstLetterLastName = [];
firstLetterLastName.push(zw[j][1].charAt(0).toLowerCase());
//Other Stuff....
}
}
}
}
I have a menu item attached to a simple onOpen, that calls menuItem1(). Calling the function prompts the user with a warning that they are about to send emails, then goes about getting data to assign email addresses based on the contents of the sheets. firstLetterLastName is an example.
I'm still not getting the loop to function, is it because I have it between two if statements? (Here is a link to the sheet)
Indeed it is quite trivial. You have mixed up your increment. You wrote
for (var j = 0; j < zw.length; ++j)
which means that you do 1 + i (and we know that at the start i = 0 which means your value will always be 1) instead of using the usual
for (var j = 0; j < zw.length; j++)
which would mean that you do i + 1 and update i, so you will get the expected 0 + 1 1 + 1 etc
EDIT:
First, I recommend instead of something like
if (responseMir === ui.Button.YES) {
// Your For loop
doing
if (responseMir !== ui.Button.YES) {
return
}
and in a similar fashion in the for loop
if (zw[j][9] !== 'Yes') {
break
}
It mostly helps increase readability by not including large blocks of code under a single if, when all you want to do is to stop execution.
Your for loop gets broken because of the mistake here:
teacherEmailMir.push(selValsMir[j][7]);
So your loop will go over once. However on the next itteration, you try to push selValsMir[1][7] which does not exist. Note that each itteration you have var selValsMir = []; inside the loop, which means that for every j selValsMir will always be an empty array. So with the following line
selValsMir.push([zw[j][0], zw[j][1], zw[j][2], zw[j][3], zw[j][4], zw[j][5], zw[j][7], zw[j][22], zw[j][23], zw[j][24]]);
your array will always have selValsMir.lenght = 1 and selValsMir[0].length = 10. So obviously trying to access anything from selValsMir[1] will throw you an error and stop the script right there.
I also recommend looking over the if statements that look at the first and first 2 letters of the name as I believe you can accomplish the same with less code. Always try to streamline. Consider using switch() where you end up using a lot of else if
I have local data delete functionality:
$.each(rowids, function() { // - delete selected rows
$grid.delRowData(this);
});
I noticed that after this call, doing retrieving the _index:
var xref = $grid.jqGrid('getGridParam', '_index');
the _index still contains the deleted row.
I looked into the JQGrid source, after the deletion of the local data, a call to refreshIndex() is made. I noticed that the Index is not removed, but rather the existing array is overwritten:
for (i = 0; i < datalen; i++) {
val = $.jgrid.getAccessor(ts.p.data[i], idname);
if (val === undefined) { val = String(i + 1); }
ts.p._index[val] = i;
}
I added the following right before the loop above:
ts.p._index = [];
This appears to resolve my issue, will this cause problems?
I think that you are right. One could change the line of delRowData to
delete $t.p._index[id];
and one should add the line
ts.p._index = [];
before the loop exactly like you suggested.
I think you should post the corresponding bug report to trirand.
Bear with me on this one cause this one is a bit tricky for me to explain.
So I have multiple observables assigned, say:-
var self = this;
self.amount = ko.observableArray();
self.data0 = ko.observable([10,11,12]);
self.data1 = ko.observable([1,2,3]);
self.data2 = ko.observable([3,4,5]);
self.data3 = ko.observable([6,7,8]);
self.data4 = ko.observable([9,10,11]);
And there is some button that changes the value on each of them with the following function (what this functions does isn't really important, it's merely to show that there is some change going on in the observables)
self.bindOneByOne = function(){
var self = this;
var i = 0;
while(self['data' + i]){
for(var j = 0, len = self['data'+i]().length; j < len; j++){
self['data'+i]()[j] *= 2;
}
self.amount.push(i);
i++;
}
};
Now what I'm wanting to do is to display the changes as it happens in the UI side, one at a time (first self.data0 and then data1 and so on..) when I call a function (click on a button in this case)
My attempt for that behavior so far:-
self.changeValues = function(){
var i = 0;
while(self['data' + i]){
setTimeout(self['data' +i].valueHasMutated,1000);
i++;
}
}
Shouldn't my code first bind self.data0 first and shouldn't it immediately reflect on my UI? Currently, I'm only seeing changes all at once which is not the behavior I wanted.
Here's the fiddle for what I'm trying to do. (Click on Populate/Change to populate the data and change it after it's been populated...and then Mutate to see the changes on the UI side. You can also see that the data is indeed changing when you press Populate/Change button if you check your console prior to clicking on Mutate button)
The key with the timeout is to capture the loop values (i and J) in a closure with the use of an IEFE(immediately invoked function execution)
for(var j = 0, len = self['data'+i]().length; j < len; j++){
(function(){
//this captures the item to set using the current value of i and J
var itemToSet = self['data'+i]()[j];
setTimeout(function(){
console.log('itemToSet',itemToSet());
itemToSet(itemToSet() * 2);
},1000*(i+1));
})() //the () brackets immediately invoke this function that is also in brackets
}
self.amount.push(i);
I've created a fiddle to show it working, you only need the 1 button to show it really, I have just made the second button update each item individually, rather than all values in the array that the first button does.
Fiddle here
Hope it helps.
This is my code:
var rowData = [];
var showFiles = function () {
var input = document.getElementById('doc-upload');
if (input.files.length > 0) {
for (var i = 0; i < input.files.length; i += 1) {
rowData.push([input.files[i].name]);
};
console.log(rowData);
};
};
document.getElementById('doc-upload').addEventListener('change', showFiles, this);
'rowData' recives a normal value for the first time i upload some thing.
At the second try it starts to duplicate incoming values.
If i uploaded 1 file first try it's ok.
Second time i wold get 2 same files.
Third 4 files, exetera.
Why this is happening?
UPD:
More explanations:
What happaning is: i upload x.doc my array is [x.doc], now i want to upload y.doc. I choose y.doc and my array turns in to [x.doc, y.doc, y.doc], then i upload z.doc and aaray looks like [x.doc, y.doc, y.doc, z.doc, z.doc, z.doc, z.doc]. And so on. It have to do some thing with length property, i messed it up some how.
UPD2:
Now the full version of the code:
//file upload
var rowData = [];
docAddButton.addListener('click', function () {
var showFiles = function () {
var input = document.getElementById('doc-upload');
if (input.files.length > 0) {
for (var i = 0; i < input.files.length; i += 1) {
if (rowData.indexOf(true, input.files[i].name, Math.round(input.files[i].size * 0.001) + ' кб') === -1) {
rowData.push([
true,
input.files[i].name,
Math.round(input.files[i].size * 0.001) + ' кб'
]);
}
}
console.log(rowData);
tableModel.setData(rowData);
};
};
document.getElementById('doc-upload').addEventListener('change', showFiles, this);
});
Thanx every one who helped!
Solution:
You can see from UPD2 that I have my function inside eventListener 'click'. What was happening is every time I pressed my input button it was reciving extra 'change' listener.
I changed .addListener for input to .addListenerOnce.
Presumably you're not reloading the page, and you're not clearing out rowData. You haven't shown how/where you define rowData, but it would appear that the same instance of it is reused between calls to showFiles. (If you haven't declared it anywhere, you're falling prey to The Horror of Implicit Globals.)
From your comments below, it sounds like you want to keep the entries in rowData, but you want to only add new entries. If so, you'll need an explicit check:
var showFiles = function () {
var input, i, file;
input = document.getElementById('doc-upload');
for (i = 0; i < input.files.length; i += 1) {
file = input.files[i].name;
if (indexOfFile(rowData, file) === -1) {
rowData.push([file]);
}
};
console.log(rowData);
};
function indexOfFile(data, file) {
var index;
for (index = 0; index < data.length; ++index) {
// Note that the 0 may need to be changed if the
// `rowData.push([file])` line above doesn't put
// the filename at index 0
if (data[index][0] === file) {
return index;
}
}
return -1;
}
Side note: You've said that the reason you're pushing an array rather than just the filename is that there's other information you're also including in that array but you left it out of your question. That's fine, but it may be delicate, see my note about index 0 above. You might consider using an array of objects instead:
rowData.push({
file: file,
other: "info",
goes: "here"
});
...and then in indexOfFile:
if (data[index].file === file) {
That way, the code is less susceptible to breaking if you change the structure of what you're pushing.
Basically,
rowData.push()
will just keep appending the elements to the end of your variable. You need to have a check if those inputs are already present.
(Edit-)
Try this:-
rowData=[];
var showFiles = function () {
var input = document.getElementById('doc-upload');
if (input.files.length > 0) {
for (var i = 0; i < input.files.length; i += 1) {
var f=[];
for(var j=0;j<rowData.length;j++)
f.push(rowData[j][0]);
if(f.indexOf(input.files[i].name) == -1)
rowData.push([input.files[i].name]);
}
console.log(rowData);
};
};
document.getElementById('doc-upload').addEventListener('change', showFiles, this);
I am creating a 'simple' javaScript function which basically displays new information on the page when a user clicks next or previous.
The information is taken from an array and I want to use either i++ or i-- to call an element of the array.
Heres my JavaScript:
var titles = ["Dundalk", "Navan", "Drogheda", "Dublin"];
var i = 0;
function next()
{
i++;
if (i == titles.length)
{
i = 0;
}
var object = document.getElementById('tname');
object.innerHTML = titles[i];
}
function prev()
{
if (i == 0)
{
i = titles.length;
}
i--;
var object = document.getElementById('tname');
object.innerHTML = titles[i];
}
The problem is, when I run this code in my HTML page, I get an 'UNDEFINED' result. The JavaScript is not recognizing that i has been initialized as 0 in the beginning.
If i change titles[i] to titles[2], for example, the correct text is displayed in HTML.
What am I forgetting or how can I overcome this?
Thanks
The fact that you're seeing undefined indicates that you're accessing an array index which hasn't been set. Your code looks fine at a glance, so I would guess that there's some more code you're not showing which also uses i as a loop variable and leaves it set to a value > titles.length after the code above has run.