I am having an issue with the following code when trying to iterate through the items in a form and delete them to make way for new sections/questions. However, I sometimes get the following error "Invalid data updating form". I have worked around this multiple times now, but it keeps coming back up. My current workaround has been to set the section title to "", which made it available to delete. Previously, I didn't need to do this until today.
My question: What is the best way to iterate through the items in a form and delete them from a starting point and not encounter this error?
Reference:
f = the current active form
f_items = all of the items of the form in an array
function clearForm()
{
var clearQ = find(f_items, "Select Appointment Date/Time")+1;
var f_i_len = f.getItems().length-1;
var clear = clearQ;
while(clear <= f_i_len && clear >= clearQ)
{
var item = f.getItems()[clear];
Logger.log(item.getTitle() + " | " + item.getType());
Logger.getLog();
if(item.getType() == "PAGE_BREAK")
{ item.asPageBreakItem().setTitle(""); }
f.deleteItem(clear);
f_i_len = f.getItems().length-1;
clear++;
}
}
function find(src, name)
{
var s_len = src.length;
for(var iter = 0; iter < s_len; iter++)
{
var s = src[iter].getTitle();
if(s == name)
{ return iter; }
}
return -1;
}
The issue I had with this was that the PageBreakItem I was trying to delete was the destination for a conditional answer earlier in the form.
Below is my code where I needed to delete everything after a certain item, which linked to the sections I needed to delete, so I was able to iterate backwards with a while loop.
function getParentNameItem_(form, form_items){
//finds the item with conditional navigation to what you want to delete
var parent_name_item = find_(form_items, "Your Name");
parent_name_item = parent_name_item.asListItem();
//clears all choices which breaks the navigation dependency
//this frees up the items to be deleted
parent_name_item.setChoices([parent_name_item.createChoice("")]);
var size = form_items.length - 1;
//iterates from the end back to the last question I want to keep
while(form_items[size].getTitle() != "Your Name"){
//this can take either the item itself or the index as I've done
form.deleteItem(size);
size--;
}
/*I rebuild the choices for parent_name_item
later based on information from a spreadsheet
which I also use to determine the content of
the PageBreakItems I just deleted*/
return parent_name_item;
}
I found out the issue! It was the clear++ at the end of the loop. With the number of items in the going down with each iteration, the clear++ was causing it to skip over the page break items. Below is my finished code:
function clearForm()
{
var clearQ = find(f_items, "Select Appointment Date")+1;
var f_i_len = f.getItems().length-1;
var clear = clearQ;
while(clear <= f_i_len && clear >= clearQ)
{
var item = f.getItems()[clear];
if(item.getType() == "PAGE_BREAK")
{ item.asPageBreakItem().setTitle(""); }
f.deleteItem(clear); //}
f_i_len = f.getItems().length-1;
}
}
I follow this tutorial to learn how to make a pagination.
https://github.com/feichao/angular-material-paging
I put a json, myData, with items to see how will work. Because I'm a junior programmer eI got blocked to gotoPage().
In my page I changed:
ctrl.total = ctrl.myData.length;
ctrl.currentPage = 1;
ctrl.step = 6;
ctrl.gotoPage = function() {
if(ctrl.total) {
for(var i=0; i < ctrl.total; i++) {
//
}
}
};
but I don't know what to put in this function to show me only, let say 10 items, from my json, per page.
You can see these props and methods in their demo.
Can someone please give ideas how to do this?
You need to calculate the starting amount and then iterate until you hit the max for that page
ctrl.total = ctrl.myData.length; //the total items in your set
ctrl.currentPage = 1;
ctrl.step = 6; //page size? this could be 10 if you want
ctrl.numpages = ctrl.total/ctrl.step; //the total number of items partitioned by page size. convert this to a whole number (the ceiling)
//go to page function here receives the indexes for that page, is that ok?
ctrl.gotoPage = function(Page) { //i modified this to take the page number as an argument
if(Page<= ctrl.numpages && Page>0) //we need to check that the page is valid
{
var startpos = ((Page-1)*(ctrl.step))+1; //this is virtually a skip amount. If its the first page, i = 0, otherwise we start with an increment of the page size. If its the second page, we skip the first 6 results
for(var i=startpos; i < startpos+ctrl.step; i++) {
if(i == ctrl.total) //if its the last page we cut it off early
break;
}
}
};
I have a problem with Qualtrics Loop and Merge + Javascript. Basically, Qualtrics reload the previous trial of my loop instead of a new one.
First, here is what I try to do: I have two sets of pictures, at each loop, one picture of each set is randomly picked and displayed (randomly to the left or right side). At the end of the loop, each pictures of the set will be displayed (33 in each set, so 33 trials of the loop), without picking twice the same picture. At each trial, I also display randomly the name of the condition "in relationship" or "friends"
The problem in details:
I have coded something that seems to work quite well, except at some point: It happens that when loading the next trial of the loop, it doesn't display new pictures but instead the one that were presented in the previous trial (and also the same name of condition). This can go for a while presenting me always the same pictures, or this can just be one time.
In any case, this trial which is represented count as a trial, as my loop always ends after 33 trials, whatever if some of these are actually not new pictures presented.
This problem doesn't appear always at the same trial, and can appear several times in a loop.
It also happened that only one picture displayed is new, and the other is the one presented in the previous trial. (I really don't get how this can happen).
I coded the randomisation of picture in an empty question, just before the question where it displays the pictures. This empty question is supposed to not require to press the "next" button. When the trial are correct, this is working well, but when the next trial is going to be one "reloaded" trial, it requires to press the "next" button.
I tried the survey with Firefox and Edge, and the problem is the same.
My javascript code:
In the first question of the survey:
Qualtrics.SurveyEngine.addOnload(function () {
function shuffle(o) { //v1.0
for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
var FacesM_list = "IM_7U04c3TQlA7dT3D,IM_eE5iBcKfoOblbJH,IM_a3mklzA9E1OuRWl,IM_bQnoSJOGwa0vn9P,IM_5inNCVgPdHHTVSR,IM_bHlurQJWSXDRDFj,IM_3rCr2DIzW2cvqLP,IM_509x1wz7pM6PP0N,IM_3UGdsp02IcCqaSV,IM_eLrY7bwDPiT7apn,IM_3LdlHnBb6tnkBEh,IM_3pY6z6JDgaDvwq1,IM_9HtZxBS79DiOfR3,IM_03c9pDSpcwcqIyF,IM_6WGKJOzUWK4TJat,IM_2rxQPEGO7SEvsY5,IM_9YN3UCUtWEWTfGR,IM_8jZTSUAGJfuVECV,IM_9nQkFhRY2cLIVgN,IM_2abcJA7B79jt30h,IM_cD31N8XPTGliUN7,IM_0eL8iQd4PVdyuQl,IM_cuOoV9gSAe6CWd7,IM_9Nv3X7lWYEsTzsF,IM_5ccXAuuomEEyamp,IM_9mnvThFiNA5U84t,IM_e3UGNNuMdrKH8cl,IM_3aggsd5P9MMlUDr,IM_4ORY6GEMW8CmNPT,IM_50WOBkz8ADTFGHH,IM_3rqtlVBfijYCccZ,IM_3CzDsr0tYv7PH5H,IM_4SmeprjDgOeCl5b"
var FacesF_list = "IM_6fkHDs5f6ItAuk5,IM_0ri9MLjDhHxyonP,IM_bKlHtoAaxnBFlnT,IM_1WUqtBPpdhERpjf,IM_ac0yWos8tAqSMNT,IM_3xCePACn1Lq97tH,IM_6o1ZPLGUM682Au1,IM_babATdN3VtBLIsl,IM_8HSUICLvFvDXaN7,IM_0ebTztq3ML5Zh0V,IM_3lB8j5dhMs8i8ip,IM_0iC0pwDlpOkcTGt,IM_cIRojwU6sx3W7Od,IM_9ZHNbignrAfcThX,IM_8iFXvVcCqk5hemN,IM_6rrwImdl4Nss0u1,IM_6mPEaoIdazwqAWp,IM_b8lrxhsPGcc1HaR,IM_23uYWeF2gYVMsap,IM_6ycfrm5xOlewjFb,IM_7UKValFGc9Kmpp3,IM_8Bbkzsmc7CyMvqt,IM_d5S95FnSgo8j06F,IM_brXT4VUU8QJiRwN,IM_9MEkpgEmOwXhril,IM_6KG9qokOlD16GDH,IM_ellgVnYbtb8ZSbb,IM_eg6qSYMQ56z5JpX,IM_5vfbDNPdZeP1XCZ,IM_cDbOprwCUUSnUZT,IM_cumIGHXOFByV4Pz,IM_0jh2Va4JTfGsQDz,IM_0CGlFRy4dW8lDcF"
var FacesM_order = [];
for (var i = 1; i <= 33; i++) {
FacesM_order.push(i);
}
FacesM_order = shuffle(FacesM_order);
var FacesF_order = [];
for (var i = 1; i <= 33; i++) {
FacesF_order.push(i);
}
FacesF_order = shuffle(FacesF_order);
var nTrial = 0;
Qualtrics.SurveyEngine.setEmbeddedData("FacesM_order", FacesM_order.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesF_order", FacesF_order.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesF_list", FacesF_list.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesM_list", FacesM_list.toString());
Qualtrics.SurveyEngine.setEmbeddedData("nTrial", nTrial.toString());
});
And in the first question of the Block with Loop and Merge:
Qualtrics.SurveyEngine.addOnload(function () {
var nTrial = Number("${e://Field/nTrial}") + 1;
var FacesF_list = "${e://Field/FacesF_list}".split(',');
var FacesM_list = "${e://Field/FacesM_list}".split(',');
var FacesF_order = "${e://Field/FacesF_order}".split(',');
var FacesM_order = "${e://Field/FacesM_order}".split(',');
var FacesF = FacesF_list[FacesF_order[nTrial]];
var FacesM = FacesM_list[FacesM_order[nTrial]];
var rand = Math.random()
if (rand < 0.5) {
var left = FacesF
var right = FacesM
} else {
var left = FacesM
var right = FacesF
}
Qualtrics.SurveyEngine.setEmbeddedData("nTrial", nTrial.toString());
Qualtrics.SurveyEngine.setEmbeddedData("left", left.toString());
Qualtrics.SurveyEngine.setEmbeddedData("right", right.toString());
this.clickNextButton.delay(.00000000000001);
});
Thanks a lot for your help!
I have the following checkbox who is calling a function on launch to get checked based on a value that comes in from an api (cat.term_id)
<input ng-checked="ifCatExcluded(cat.term_id)">
The function works as follows: The first bit sets the checkbox value to true and gets an array which i need to cross check the id.
$scope.ifCatExcluded = function(sent_id){
var doesNotExist = true;
arraytosearch = JSON.parse(localStorage.getItem("categoryListMem"));
The second bit creates an empty array and adds the cat.term_id to this new array to avoid re running this id later.
if($scope.tempArray == undefined || $scope.tempArray.indexOf(undefined) > 0) {
$scope.tempArray = [];
}
if ($scope.tempArray.indexOf(sent_id) < 0) {
$scope.tempArray.push(sent_id);
console.log($scope.tempArray);
for (var i = 0; i < arraytosearch.length; i++) {
if (arraytosearch[i]['id'] == sent_id) {
doesNotExist = false;
console.log(sent_id + " IS EXCLUDED");
}
}
}
Then the last part returns the value for the checkbox and here is the problem. I cannot get a return of true or false from the doesNotExist variable. I noticed that when I take
for (var i = 0; i < arraytosearch.length; i++) {" ecc...
out of the if statement everything works...
return doesNotExist;
console.log(doesNotExist);
};
Any help is much appreciated.
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);