So I've got a couple of questions. I'm attempting to create a dynamic spreadsheet, with the capabilities of having values inserted into it as well as (and only needed) being able to process the SUM() formula. The problem I've run into right now, is when inserting a value.
Firstly allow em to show the JS I have for the insertion. The HTML table is using id values of 1_1,1_2 etc.
EDIT: Fixed the var column= document.getElementById(insert); line, however it doesn't seem to want to commit any inserted value. Shouldn't it simply be a return statement to commit a change to the html field?
function insertValue() {
var v = document.getElementById("submitText");
v = v.value;
var row = document.getElementById("row");
row = row.value;
var col = document.getElementById("col");
col = col.value;
var insert = row + "_" + col;
insert = insert.toString();
var col = document.getElementById("col");
EDIT:: return column.value = v;
} //end function
When I'm attempting to take the insert variable and use it as the id value, it breaks the whole program. Should I be casting the insert var to something other then a string?
I assume
var column.getElementById(insert);
should be
var column = document.getElementById(insert);
Your line of code is invalid.
function insertValue() {
var v = document.getElementById("submitText");
v = v.value;
var row = document.getElementById("row");
row = row.value;
var col = document.getElementById("col");
col = col.value;
var insert = row + "_" + col;
// Don't really think this is necessary, so probably remove it.
//insert = insert.toString();
var column = document.getElementById(insert); // Compare this line to yours
column.value = v;
} //end function
The reason of having error is because you're not writing it correctly.
var column.getElementById(insert); is wrong syntax. var is supposed to be followed by a variable name, and you can assign an initial value to it.
It is surprising that though you got it correct in the beginning but fail to do so later.
But the next time you have an error in your JavaScript code, check the browser's JavaScript Console. It can probably be opened using F12, and inside you will see the error is shown. Then you will not need to always come up and ask a question for every single problem you met. :)
Related
I've been using this function to erase certain rows depending on the name of a cell in a column, for some reason it works, but after it finishes it just gives me an error message.
Exception: Service Spreadsheets failed while accessing document with id #################################################.
function erase() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var range = 'J:J';
var object = 'Delete';
var name = 'Roster';
var sheet = spreadsheet.getSheetByName(name);
var row = sheet.getRange(range);
var value = row.getValues();
for (var i = value.length - 1; i >= 0; i--)
if (value[0, i] == object)
sheet.deleteRow(i + 1);
}
This looks to be a known issue from Google's side which has been reported on Issue Tracker. I suggest you star the issue from here and eventually add a comment saying that you are affected by it.
As a possible workaround, you can try adding SpreadsheetApp.flush() before this line here var row = sheet.getRange(range);.
I'm not familiar with google sheets specifically, but just with general syntax knowledge, I feel like the issue would surround, var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();. Are you sure you arn't supposed to put anything inside the getActiveSreadshee(-here-)??
How can I copy an array so that it copies into each cell going down Column F, instead of going across a certain row, like I have in my code now. In this case it's row "7". How can I do this and start at a certain cell, let's say F3. I am currently using ExcelJS, but would be open to try other things.
Index.Js
var Excel = require('exceljs');
var captureNames = [1,2,3,A,B,C];
workbook.xlsx.readFile("X:\\TESTING_DATA\\eclipse-database\\myExcelFile.xlsx")
.then(function() {
var sheet = workbook.addWorksheet('My Sheets');
var worksheet = workbook.getWorksheet("My Sheets");
var row = worksheet.getRow(7);
row.values = captureNames;
return workbook.xlsx.writeFile("X:\\TESTING_DATA\\eclipse-database\\myExcelFile.xlsx");
})
What I get now
What I want
Didn't test because of lack of example file, but you should do something similar:
captureNames.forEach(function(name, i) {
worksheet.getCell('F' + (3 + i)).value = name
})
Just had to add a formula like this, thanks #dotsu !
captureNames.forEach(function(name, i) {
worksheet.cell('F' + (3 + i)).value(name);
})
I'm having an issue pulling the correct values out of a for loop in Google Sheets.
Here's my code:
Note: this is a snippet from a larger function
function sendEmails() {
var trackOriginSheet = SpreadsheetApp.getActiveSpreadsheet().getName();
var getMirSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Miranda");
//Set a new object to hold conditional data
var holdingData = new Object();
//Create function to get values from origin sheet
var returnedValues = function (trackOriginSheet) {
//Load dynamic variables into an object via returnedValues()
if (trackOriginSheet === getMirSheet) {
var startMirRow = 2; // First row of data to process
var numRowsMir = 506; // Number of rows to process
// Fetch the range of cells A2:Z506
var dataRangeMir = getMirSheet.getRange(startMirRow, 1, numRowsMir, 26);
// Fetch values for each cell in the Range.
var dataMir = dataRangeMir.getValues();
for (var k in dataMir) {
var secondRowMir = dataMir[k];
var intRefDescMir = secondRowMir[3];
var intAdminActionsMir = secondRowMir[4];
//Push returned data to holdingData Object
holdingData.selectedData = secondRowMir;
holdingData.refDesc = intRefDescMir;
holdingData.adminActions = intAdminActionsMir;
}
}
}
Here's a copy of the sheet I'm working on
What I need to have happened here first, is track the origin sheet, then create an object to hold data returned from the returnedValues() function. Later, I'll call the properties of this object into a send email function.
The problem is that I need to be able to pull data from the selected sheet dynamically (the "Miranda" sheet in this case.) In other words, when a user selects the "Yes" option in column I of the Miranda sheet, the first thing this script needs to do is pull the values of the variables at the top of the for loop within the same row that the user selected "Yes." Then, I'm pushing that data to a custom object to be called later.
It's apparent to me, that I'm doing it wrong. There's, at least, something wrong with my loop. What have I done? :)
EDIT:
After reviewing the suggestion by VyTautas, here's my attempt at a working loop:
for (var k = 0; k < dataMir.length; k++) {
var mirColI = dataMir[k][8];
var mirRefDesc = dataMir[k][2];
var mirAdminActions = dataMir[k][3];
var mirDates = dataMir[k][4];
if (mirColI === "Yes") {
var activeRowMir = mirColI.getActiveSelection.getRowIndex();
//Pull selected values from the active row when Yes is selected
var mirRefDescRange = getMirSheet.getRange(activeRowMir, mirRefDesc);
var mirRefDescValues = mirRefDescRange.getValues();
var mirAdminActionsRange = getMirSheet.getRange(activeRowMir, mirAdminActions);
var mirAdminActionsValues = mirAdminActionsRange.getValues();
var mirDatesRange = getMirSheet.getRange(activeRowMir, mirDates);
var mirDatesValues = mirAdminActionsRange.getValues();
var mirHoldingArray = [mirRefDescValues, mirAdminActionsValues, mirDatesValues];
//Push mirHoldingArray values to holdingData
holdingData.refDesc = mirHoldingArray[0];
holdingData.adminActions = mirHoldingArray[1];
holdingData.dates = mirHoldingArray[2];
}
}
Where did all that whitespace go in the actual script editor? :D
You already correctly use .getValues() to pull the entire table into an array. What you need to do now is have a for loop go through dataMir[k][8] and simply fetch the data if dataMir[k][8] === 'Yes'. I also feel that it's not quite necessary to use for (var k in dataMir) as for (var k = 0; k < dataMir.length; k++) is a lot cleaner and you have a for loop that guarantees control (though that's probably more a preference thing).
You can also reduce the number of variables you use by having
holdingData.selectedData = mirData[k]
holdingData.refDesc = mirData[k][2] //I assume you want the 3rd column for this variable, not the 4th
holdingData.adminActions = mirData[k][3] //same as above
remember, that the array starts with 0, so if you mirData[k][0] is column A, mirData[k][1] is column B and so on.
EDIT: what you wrote in your edits seems like doubling down on the code. You already have the data, but you are trying to pull it again and some variables you use should give you an error. I will cut the code from the if, although I don't really see why you need to both get the active sheet and sheet by name. If you know the name will be constant, then just always get the correct sheet by name (or index) thus eliminating the possibility of working with the wrong sheet.
var titleMirRows = 1; // First row of data to process
var numRowsMir = getMirSheet.getLastRow(); // Number of rows to process
// Fetch the range of cells A2:Z506
var dataRangeMir = getMirSheet.getRange(titleMirRows + 1, 1, numRowsMir - titleMirRows, 26); // might need adjusting but now it will only get as many rows as there is data, you can do the same for columns too
// Fetch values for each cell in the Range.
var dataMir = dataRangeMir.getValues();
for (var k = 0; k < dataMir.length; k++) {
if (dataMir[k][7] === 'Yes') { //I assume you meant column i
holdingData.refDesc = dataMir[k] //this will store the entire row
holdingData.adminActions = dataMir[k][3] //this stores column D
holdingData.dates = dataMir[k][4] //stores column E
}
}
Double check if the columns I have added to those variables are what you want. As I understood the object stores the entire row array, the value in column called Administrative Actions and the value in column Dates/Periods if Applicable. If not please adjust accordingly, but as you can see, we minimize the work we do with the sheet itself by simply manipulating the entire data array. Always make as few calls to Google Services as possible.
I ask the same matrix table (multiple answer) question twice in my Qualtrics survey. When I display the question to the user the second time, I want the answers to be auto populated from the first time the user answered the question. I've looked at the code snippets in the documentation, so I know how to select the checkboxes in the current question, e.g.
for (var i = 1; i <= rows; i++) {
for (var j = 1; j <= cols; j++) {
this.setChoiceValue(i, j, true);
}
}
My issue is that I can't figure out how to get the selected answers from the previous question. I have tried something like this but it doesn't work (cannot read property 'attr' of null):
var isChecked = $("#QR~QID2~" + i + "~" + j").attr('checked');
This page from the documentation suggests using piped text, something along the lines of:
var selectedChoice = "${q://QID2/ChoiceGroup/SelectedChoices}";
This seems to give me the unique statements in the rows of the survey that have been selected, but I need to get the selected answer (col) for each statement (row) in the question. I'm not sure how to formulate the correct piped text.
Any advice would be much appreciated! Thanks.
Edit 1: Here is the code I ended up using.
Qualtrics.SurveyEngine.addOnload(function()
{
/*Place Your Javascript Below This Line*/
var numChecks = $(this.getQuestionContainer()).select('input[type="checkbox"]');
var numCols = 4;
var numRows = numChecks.length / numCols;
var map = {};
//I won't have more than 20 rows in the matrix
map[1] = "${q://QID53/SelectedAnswerRecode/1}";
map[2] = "${q://QID53/SelectedAnswerRecode/2}";
map[3] = "${q://QID53/SelectedAnswerRecode/3}";
map[4] = "${q://QID53/SelectedAnswerRecode/4}";
map[5] = "${q://QID53/SelectedAnswerRecode/5}";
map[6] = "${q://QID53/SelectedAnswerRecode/6}";
map[7] = "${q://QID53/SelectedAnswerRecode/7}";
map[8] = "${q://QID53/SelectedAnswerRecode/8}";
map[9] = "${q://QID53/SelectedAnswerRecode/9}";
map[10] = "${q://QID53/SelectedAnswerRecode/10}";
map[11] = "${q://QID53/SelectedAnswerRecode/11}";
map[12] = "${q://QID53/SelectedAnswerRecode/12}";
map[13] = "${q://QID53/SelectedAnswerRecode/13}";
map[14] = "${q://QID53/SelectedAnswerRecode/14}";
map[15] = "${q://QID53/SelectedAnswerRecode/15}";
map[16] = "${q://QID53/SelectedAnswerRecode/16}";
map[17] = "${q://QID53/SelectedAnswerRecode/17}";
map[18] = "${q://QID53/SelectedAnswerRecode/18}";
map[19] = "${q://QID53/SelectedAnswerRecode/19}";
map[20] = "${q://QID53/SelectedAnswerRecode/20}";
for (var i = 1; i <= numRows; i++) {
//Get the recode values for row i
var rowValues = map[i].split(",");
//Loop through all the recode values for the current row
for (var c = 0 ; c < rowValues.length; c++) {
var val = parseInt(rowValues[c].trim());
//Select the current question's checkboxes corresponding to the recode values
this.setChoiceValue(i, val, true);
}
}
});
Edit 2:
I'm getting some strange behavior now. I'm trying to populate a table with only 3 rows, so I would think that
"${q://QID53/SelectedAnswerRecode/1}";
"${q://QID53/SelectedAnswerRecode/2}";
"${q://QID53/SelectedAnswerRecode/3}";
would give me the values for the first three rows from the previous table for question "QID53" . But actually those return empty strings, and it's not until calling
"${q://QID53/SelectedAnswerRecode/5}";
"${q://QID53/SelectedAnswerRecode/6}";
"${q://QID53/SelectedAnswerRecode/7}";
that I get the first three values.
For a table of 14 rows, nothing returns until calling
"${q://QID53/SelectedAnswerRecode/4}";
and it leaves the last 3 rows in the table empty.
Am I wrong in assuming that the number after "SelectedAnswerRecode" is the row number? Is there something about an offset that I'm missing?
I think you'll want to pipe in the recode values. Something like:
var r1ar = parseInt("${q://QID2/SelectedAnswerRecode/1}");
var r2ar = parseInt("${q://QID2/SelectedAnswerRecode/2}");
Then set the values:
this.setChoiceValue(1, r1ar, true);
this.setChoiceValue(2, r2ar, true);
Make sure your recode values match the column ids.
Edits/additions based on comments below:
Piped values in the javascript get resolved server side before the page is sent to the browser, so they are fixed values. There is no way to make them dynamic in javascript. If your question has a variable number of rows due to display logic or carryover, you'll have to include all the possible piped values in the javascript, then check them to see which ones are valid.
For a multiple answer matrix, you can convert the comma separated list into an array using str.split(',') then loop through the array. Qualtrics includes spaces after the commas in comma separated lists, so you'll have to trim() the strings in the array.
I have a working script that upon form submit, specific rows move from one sheet to another. One of the fields I'm pushing is a url.
On the second sheet, the link is listed and it is hyperlinked, but it's really ugly and I really want to format it so that it shows "Edit" with a hyperlink. I've tried a number of ways, but my knowledge is limited so all I get are errors. I'm hoping someone can point me in the right direction.
Here is my code. I'm very new at this so the script is not at all sophisticated. Any help/suggestions would be appreciated!
function copyAdHoc(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = SpreadsheetApp.setActiveSheet(ss.getSheetByName("Form Responses 1"));
var data = sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn()).getValues();
// Grab the Headers from master sheet
var headers = sh.getRange(1,1,1,sh.getLastColumn()).getValues();
var date = headers[0].indexOf('Effective Date');
var name = headers[0].indexOf('Employee Name');
var loc = headers[0].indexOf('Location');
var issue = headers[0].indexOf('Description/Question/Issue');
var add = headers[0].indexOf('Additional Information');
var change = headers[0].indexOf('Is this a Qualifying Life Event?');
var url = headers[0].indexOf('Form URL');
var category = headers[0].indexOf('Primary Category');
var status = headers[0].indexOf('Current Status');
var users = headers[0].indexOf('Users');
// Grab only the relevant columns
for(n = 0; n < data.length; ++n ) { // iterate in the array, row by row
if (data[n][change] !== "Yes" & data[n][category] !== "Employee Relations" & data[n][date] !== "") { // if condition is true copy the whole row to target
var arr = [];
arr.push(data[n][url]);
arr.push(data[n][users]);
arr.push(data[n][date]);
arr.push(data[n][loc]);
arr.push(data[n][name]);
arr.push(data[n][category]);
arr.push(data[n][issue] + ". " + data[n][add]);
arr.push(data[n][status]);
var sh2 = SpreadsheetApp.setActiveSheet(ss.getSheetByName("Ad Hoc")); //second sheet of your spreadsheet
sh2.getRange(sh2.getLastRow()+1,2,1,arr.length).setValues([arr]); // paste the selected values in the 2cond sheet in one batch write
}
}
}
It's a bit messy but the only way I know to achieve what you're trying to do would be to insert a column to the left of the hyperlink with the word Edit right justified and then remove the borders between the two.
From your description I am assuming you want the word "Edit" to be Hyperlinked. To do so, try this:
function getHyperlink(url)
{
return "=HYPERLINK(\""+url+"\","+"\"Edit\""+")";
}
function mainFunct()
{
//Do necessary steps
var tarLink = "https://www.google.com";
var tarRng = tarSheet.getRange(rowNum, colNum).setValue(getHyperlink(tarLink));
//perform other steps
}
EDIT:
Forgot to mention, since you're pushing your values to the array... you can do it in a similar way by either just storing the hyperlink in a variable or directly pushing it to the array like all the other values. Or if you're dealing with a hyperlink that has a static and dynamic part, For example: https://stackoverflow.com/questions/post_id, where post_id keeps changing but most of the URL is static, you can easily handle it by just passing the post_id to the getHyperlink function and getting the required Hyperlink in return. Hope this helps.