I'm relatively new to Google Scripts for Sheets/Forms, and have edited this answer to create a script that updates the options on a dropdown question whenever it is triggered. It works wonderfully, but with one small glitch. My code as below:
var idColumn = 1;
function getSheet() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("ProjectList");
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var lr = rows.getLastRow();
var docId = sheet.getRange(2,idColumn,lr,1).getValues(); //gets the data from the last row in selected column
fillFormData(docId);
}
//fills form with document ID
function fillFormData(docId) {
var formId = 'form ID goes here'
var form = FormApp.openById(formId);
var items = form.getItems();
var item = items[1];
//loop variables
var thisValue = "";
var arrayOfItems = [];
var newItem = "";
for (var i=0;i<docId.length;i++) {
thisValue =docId[i][0];
Logger.log('thisValue: ' + thisValue);
newItem = item.createChoice(thisValue);
arrayOfItems.push(newItem);
};
item.setChoices(arrayOfItems)
}
My hope was that, by updating the question using setChoices(), responses would be placed in the same column in the response sheet. Instead, each trigger of this script creates another column in the response sheet named "Project Number", overwriting one of the columns that were there before. Is this an expected behaviour, or does anyone know of a way to ensure that my responses go into the same column? Is there a way to direct responses to this question to a specific column in the response sheet?
Edit: As per the suggestions below, I've tried adjusting the macro to use .asListItems() with no success. The interesting behaviour here is that the new, duplicate column seems to overwrite one of my other columns rather than create a new one.
Here's my ProjectList, just a single column:
Project Number
Project A
Project B
Project C
Project D
Project E
The form consists of 35 questions, of a variety of types, split among 3 sections.
I think you need to explicitly set the question you're trying to add the choices to, please see below example of working script to add new items to form.
function getSheet() {
var sheet = SpreadsheetApp.openById('sheet id here');
}
var formId = "form id here";
var question = [{formFieldTitle:"question title here", worksheetName:"linked form sheet name here"}]
function newChoices(item, sheetName) {
var data = (SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName(sheetName)
.getDataRange()
.getValues());
var choices = [];
for (var i = 1; i < data.length; i += 1){
choices.push(item.createChoice(data[i][0]));
}
item.setChoices(choices);
}
function addNewChoices() {
var form = FormApp.openById(formId);
var items = form.getItems();
for (var i = 0; i < items.length; i += 1) {
for (var j = 0; j < question.length; j += 1) {
var item = items[i];
if (item.getTitle() === question[j].formFieldTitle) {
addNewChoices(item.asCheckboxItem(), question[j].worksheetName);
break;
}
}
}
}
Variable "question" should have the question header and the sheet name from which your form items are being added.
var question = [{formFieldTitle:"question title here", worksheetName:"sheet name here"}]
Also, make sure to change "asCheckboxItem" if your question is not a checkbox in the line below, see Interface Item documentation to determine what to use.
addNewChoices(item.asCheckboxItem(), question[j].worksheetName);
Instead of using method createChoice() and setChoices(), you should use setChoiceValues() so it doesn't create a new question. By using setChoiceValues(), you are effectively updating the choices.
See the code below:
function optWriteItem_CHECKBOX_(paramItem, paramItemData) {
try {
var thisItem = paramItem.asCheckboxItem();
var listChoices;
var n, i;
n = paramItemData.length;
if(paramItemData.indexOf('__OTHER__') > 5) {
listChoices = paramItemData.slice(5, n-1);
thisItem.showOtherOption(true);
} else {
listChoices = paramItemData.slice(5, n);
thisItem.showOtherOption(false);
}
thisItem.setTitle(paramItemData[0]);
if(paramItemData[3].toLowerCase() == 'yes') thisItem.setRequired(true);
else thisItem.setRequired(false);
thisItem.setHelpText(paramItemData[4]);
if(listChoices.length == 0) listChoices = [ 'Option 1' ];
thisItem.setChoiceValues(listChoices);
} catch(err) {
Logger.log('optWriteAsType/opt=CHECKBOX : ' + err.message);
console.error("optWriteItem_CHECKBOX_()", err);
return {s:false, m:err.message};
}
return {s:true};
}
Source-code
Related
I've got a code in Google Apps Script to automatically populate a Google Form. It works perfectly, but there's one big problem. When I run the script multiple times, the Forms copies itself. So, instead of overwriting the previous run, it adds a whole new (identical) Form. For example: after running the code one time, it shows 4 sections. But when I run it two times, it shows 8 sections, and so on.
Does anybody know how to solve this?
Here's the used code:
function rosterMaker() {
//spreadsheet id of the rosters
var SHEET_ID = FormApp.getActiveForm().getDestinationId();
var ss = SpreadsheetApp.openById(SHEET_ID)
var form = FormApp.getActiveForm();
var sheets = ss.getSheets().filter(function(sheet) {return sheet.getName().match(/Roster/gi);});
//add multiple choice item
var classSelect = form.addMultipleChoiceItem();
classSelect.setTitle('Choose a class');
var classChoices = [];
for(var i = 0; i < sheets.length; i++) {
var className = sheets[i].getName();
var classSection = form.addPageBreakItem()
.setTitle(className)
.setGoToPage(FormApp.PageNavigationType.SUBMIT);
var students = getStudents(sheets[i]);
var studentSelect = form.addCheckboxItem()
.setTitle(className + 'absent')
.setHelpText('Select the students who are absent from this class');
var studentChoices = [];
for(var j = 0; j < students.length; j++) {
studentChoices.push(studentSelect.createChoice(students[j]));
}
studentSelect.setChoices(studentChoices);
classChoices.push(classSelect.createChoice(className, classSection));
}
classSelect.setChoices(classChoices);
}
function getStudents(sheet) {
var studentValues = sheet.getDataRange().getValues();
var students = [];
for(var i = 1; i < studentValues.length; i++) {
students.push(studentValues[i].join(' '));
}
return students;
}
What you need to do is remove all the items you populated in your form before populating it again. Best place to add it on should be before adding anything to the form.
Based on your script, you can add it after var form = FormApp.getActiveForm();
See my sample code below on how it works.
var formID = <FORM_ID>;
var fData = FormApp.openById(formID);
function clearForm(){
// function that clears all items in your form
var items = fData.getItems();
// always delete while there is remaining item
while(items.length > 0){
fData.deleteItem(items.pop());
}
}
function populateForm() {
// call clearForm to prevent duplicating populated questions
clearForm();
// section to populate your form
// ....
}
If you have any issues/problems on making the code work, feel free to comment below.
I used a previous answer (thanks kessy!) to split 7000 or so rows into 40 or so different tabs based upon values in a column. I ran the same script on another nearly identical file and I get the error "TypeError: Cannot read property 'getRange' of null (line 5, file "Code")". I tried with a greatly simplified file and get the same error. Any help getting this to work is very much appreciated.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
// This var will contain all the values from column C -> Room
var columnRoom = sheet.getRange("C:C").getValues();
// This var will contain all the rows
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
//Set the first row as the header
var header = rows[0];
//Store the rooms already created
var completedRooms = []
//The last created room
var last = columnRoom[1][0]
for (var i = 1; i < columnRoom.length; i++) {
//Check if the room is already done, if not go in and create the sheet
if(!completedRooms.includes(columnRoom[i][0])) {
//Set the Sheet name = room (except if there is no name, then = No Room)
if (columnRoom[i][0] === "") {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("No Room");
} else {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(columnRoom[i][0]);
}
//append the header
currentSheet.appendRow(header);
currentSheet.appendRow(rows[i]);
completedRooms.push(columnRoom[i][0])
last = columnRoom[i][0]
} else if (last == columnRoom[i][0]) {
// If the room's sheet is created append the row to the sheet
var currentSheet = SpreadsheetApp.getActiveSpreadsheet()
currentSheet.appendRow(rows[i]);
}
}
}
I am not sure what is exactly your goal, but based on the error message you are getting it seems that you are not getting the active sheet properly. Instead, I would suggest you to specify the sheet by its name. Let's assume the desired name of the sheet you want to get is Sheet1. Then, in the first line of your function you can replace this:
var sheet = SpreadsheetApp.getActiveSheet();
with this:
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
I also optimized your code a little by removing all the unnecessary SpreadsheetApp.getActiveSpreadsheet() calls:
function myFunction() {
var ss = SpreadsheetApp.openById("SpreadsheetId");
var sheet = ss.getSheetByName('Sheet1');
// This var will contain all the values from column C -> Room
var columnRoom = sheet.getRange("C:C"+sheet.getLastRow()).getValues();
// This var will contain all the rows
var rows = sheet.getDataRange().getValues();
//Set the first row as the header
var header = rows[0];
//Store the rooms already created
var completedRooms = []
//The last created room
var last = columnRoom[1][0]
for (var i = 1; i < columnRoom.length; i++) {
//Check if the room is already done, if not go in and create the sheet
if(!completedRooms.includes(columnRoom[i][0])) {
//Set the Sheet name = room (except if there is no name, then = No Room)
if (columnRoom[i][0] === "") {
var currentSheet = ss.insertSheet("No Room");
} else {
var currentSheet = ss.insertSheet(columnRoom[i][0]);
}
//append the header
currentSheet.appendRow(header);
currentSheet.appendRow(rows[i]);
completedRooms.push(columnRoom[i][0])
last = columnRoom[i][0]
} else if (last == columnRoom[i][0]) {
// If the room's sheet is created append the row to the sheet
sheet.appendRow(rows[i]);
}
}
}
You can also run a loop within the loop and keep things server side for a faster result (at least it worked for me, I was having trouble with long spreadsheets timing out).
You have to know how many columns you want to pass over, maybe there is a better way to push the values than I have done (I only dabble in script).
function splitSheets() {
var theWorkbook = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = theWorkbook.getSheetByName("Master");
//Let's delete any sheets that were previously split, so we can rerun the script again and again
var sheets = theWorkbook.getSheets();
for (i = 0; i < sheets.length; i++) {
switch(sheets[i].getSheetName()) {
case "Master":
break;
default:
theWorkbook.deleteSheet(sheets[i]);
}
}
// This var will contain all the values from column C -> Your splitting Key
var key = theSheet.getRange("C:C").getValues();
// This var will contain all the rows
var rows = theSheet.getDataRange().getValues();
//Set the first row as the header, get the range so we can keep the formatting
var headerFormat = theSheet.getRange("2:2");
//Store the rooms already created
var completedSheets = [];
//We start at i=2 because we're on row 3, row zero for the button, row one for the header
for (var i = 2; i < key.length; i++) {
//We don't want to run the loop if we've already created the blank page and the row key is also blank.
if(completedSheets.includes('Blank') && key[i][0] === ""){
//do nothing
}else{
//Check if the room is already done, if not go in and create the sheet
if(!completedSheets.includes(key[i][0]) ) {
//Set the Sheet name = unique key (except if there is no name, then = Blank)
if (key[i][0] === "") {
var currentSheet = theWorkbook.insertSheet("Blank");
} else {
var currentSheet = theWorkbook.insertSheet(key[i][0]);
}
//To avoid pasting formulas, we have to paste contents, copying allows us to keep formatting
headerFormat.copyTo(currentSheet.getRange(1,1),{contentsOnly:true});
headerFormat.copyTo(currentSheet.getRange(1,1),{formatOnly:true});
//Now here find all the rows containing the same key address and push them, this way doing it server side
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++) {
if((rows[j][2] == key[i][0]) || (rows[j][2] === '' && currentSheet.getName() == "Blank")){
theNewRows[b]=[];//Initial new array
theNewRows[b].push(rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7],rows[j][8]);
b++;
}
}
var outrng = currentSheet.getRange(2,1,theNewRows.length,9);//Make the output range the same size as the output array
outrng.setValues(theNewRows);
//The new sheet name gets added to the completed sheets list and the value of var last is updated in prep of next step
if(currentSheet.getSheetName() == 'Blank') {
completedSheets.push('Blank');
last = "Blank";
}else{
completedSheets.push(key[i][0])
last = key[i][0]
}
}
}
}
//And return to the Master
SpreadsheetApp.setActiveSheet(theWorkbook.getSheetByName('Master'));
}
Example here, just click the button on the page
https://docs.google.com/spreadsheets/d/1pfeU2CFDbZbA4O0b4z80l5MyCKDNQnUdkpKlzODbAiI/edit?usp=sharing
It's not perfect, but hope it helps.
The following script will create a form with 4 sections. The first section of the form lists three options (dropdown) and depending on which one you choose, it will jump to the relevant section to request more information about that particular option (more dropdowns).
I am populating the first section with the names of three sheets (sql_db_info, oracle_db_info, imanis_db_info)
I am populating the three different dependant sub sections using the rows in the first two columns of each sheet.
All of this works.
The problem I have is what I need to do to update the different sections if I change any values in the columns of the existing sheets or if I add new sheets.
If I add more sheets (mongo_db_info, casandra_db_info) and their associated dependent sub sections (using the values in the new sheets), it appends to the existing form (adding more sections).
I tried to delete and recreate the sections each time but that screws up the collection of the responses in the relevant sheet. New columns are created and it spreads out of control.
So....I need to be able to just update the relevant sections by adding new entries and removing deleted entries or by overwriting the dropdown entries without recreating the sections.
So, the looping has to work in case I add new sheets (new database type with new dependent sections) but recreating the Form each time wont cut it.
So. Additional sheets, will add to the additional first section (sql, oracle, imanis, mongo, cassandra etc).
And the cell values in the first two columns of those sheets will provide the dropdowns for the subsequent two sections for each option chosen in the first section (sql,oracl,imanis etc).
How can I update the relevant dropdowns when I add new sheets or add/delete values from the columns in the sheets?
In the below code, the dbMaker function works on the first run.
What do I need to do to get it to just update dropdowns on subsequent runs?
var ssID = "url id is put here";
var formID = "url id is put here"
var ss = SpreadsheetApp.openById(ssID);
var form = FormApp.openById((formID));
//function clearAll(form){
// var form = FormApp.openById((formID));
// var items=form.getItems();
// Logger.log(items);
// //if(items < 1 ) {
// var d=0;//deleted items counter
// for (var i=0; i<items.length; i++) {
// form.deleteItem(i-d++);
//
// }
//}
// dbMaker();
//}
function dbMaker() {
//var message = 'The current time is ' + new Date().toString();
//Logger.log(message);
var sheets = ss.getSheets().filter(function(sheet) {return sheet.getName().match(/db_info/gi);});
var dbSelect = form.addListItem().setTitle('DB Type').setRequired(true);
var dbChoices = [];
for(var i = 0; i < sheets.length; i++) {
var dbName = sheets[i].getName();
var dbSection = form.addPageBreakItem().setTitle(dbName).setGoToPage(FormApp.PageNavigationType.SUBMIT);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
var nativetech = getnativetech(sheets[i]);
var nativetech_Select = form.addListItem().setTitle('Native Technology' + ' Choice').setHelpText('Select the correct DB type').setRequired(true);
var nativetech_Choices = [];
for(var j = 0; j < nativetech.length; j++) {
nativetech_Choices.push(nativetech_Select.createChoice(nativetech[j]));
Logger.log('nativetech choices', nativetech[j]);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
var integratedtech = getintegratedtech(sheets[i]);
var integratedtech_Select = form.addListItem().setTitle('Intregrated with company' + ' Choice').setHelpText('Select the correct option').setRequired(true);
var integratedtech_Choices = [];
for(var k = 0; k < integratedtech.length; k++) {
integratedtech_Choices.push(integratedtech_Select.createChoice(integratedtech[k]));
Logger.log('integratedtech choices', integratedtech[k]);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
nativetech_Select.setChoices(nativetech_Choices).setRequired(true);
integratedtech_Select.setChoices(integratedtech_Choices).setRequired(true)
dbChoices.push(dbSelect.createChoice(dbName, dbSection));
}
dbSelect.setChoices(dbChoices).setRequired(true);
}
function getnativetech(sheet) {
var result1 = ""
var nativetech_Values = sheet.getDataRange().getValues();
var result1 = nativetech_Values.reduce(function(ar, e) {
if (e[0]) ar.push(e[0])
return ar;
}, []);
var nativetech = [];
for(var i = 1; i < result1.length; i++) {
Logger.log('result1',result1[i]);
nativetech.push(nativetech_Values[i][0]);
}
return nativetech;
}
function getintegratedtech(sheet) {
var result2 = ""
var integratedtech_Values = sheet.getDataRange().getValues();
var result2 = integratedtech_Values.reduce(function(ar, e) {
if (e[1]) ar.push(e[1])
return ar;
}, []);
var integratedtech = [];
for(var i = 1; i < result2.length; i++) {
Logger.log('result2',result2[i]);
integratedtech.push(integratedtech_Values[i][1]);
}
return integratedtech;
}
I have searched many possible answers but cannot seem to find one that works. I have a Google Sheet with about 1600 rows that I need to split into about 70 different tabs (with about 20-30 rows in each one) based on the value in the column titled “room”. I have been sorting and then cutting and pasting but for 70+ tabs this is very tedious.
I can use the Query function but I still need to create a new tab, paste the function and update the parameter for that particular tab.
This script seemed pretty close:
ss = SpreadsheetApp.getActiveSpreadsheet();
itemName = 0;
itemDescription = 1;
image = 2;
purchasedBy = 3;
cost = 4;
room = 5;
isSharing = 6;
masterSheetName = "Master";
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Update Purchases')
.addItem('Add All Rows To Sheets', 'addAllRowsToSheets')
.addItem('Add Current Row To Sheet', 'addRowToNewSheet')
.addToUi();
}
function addRowToNewSheet() {
var s = ss.getActiveSheet();
var cell = s.getActiveCell();
var rowId = cell.getRow();
var range = s.getRange(rowId, 1, 1, s.getLastColumn());
var values = range.getValues()[0];
var roomName = values[room];
appendDataToSheet(s, rowId, values, roomName);
}
function addAllRowsToSheets(){
var s = ss.getActiveSheet();
var dataValues = s.getRange(2, 1, s.getLastRow()-1, s.getLastColumn()).getValues();
for(var i = 0; i < dataValues.length; i++){
var values = dataValues[i];
var rowId = 2 + i;
var roomName = values[room];
try{
appendDataToSheet(s, rowId, values, roomName);
}catch(err){};
}
}
function appendDataToSheet(s, rowId, data, roomName){
if(s.getName() != masterSheetName){
throw new Error("Can only add rows from 'Master' sheet - make sure sheet name is 'Master'");
}
var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
var roomSheet;
if(sheetNames.indexOf(roomName) > -1){
roomSheet = ss.getSheetByName(roomName);
var rowIdValues = roomSheet.getRange(2, 1, roomSheet.getLastRow()-1, 1).getValues();
for(var i = 0; i < rowIdValues.length; i++){
if(rowIdValues[i] == rowId){
throw new Error( data[itemName] + " from row " + rowId + " already exists in sheet " + roomName + ".");
return;
}
}
}else{
roomSheet = ss.insertSheet(roomName);
var numCols = s.getLastColumn();
roomSheet.getRange(1, 1).setValue("Row Id");
s.getRange(1, 1, 1, numCols).copyValuesToRange(roomSheet, 2, numCols+1, 1, 1);
}
var rowIdArray = [rowId];
var updatedArray = rowIdArray.concat(data);
roomSheet.appendRow(updatedArray);
}
But I always get an unexpected token error on line 51 or 52:
var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
(And obviously the column names, etc. are not necessarily correct for my data, I tried changing them to match what I needed. Not sure if that was part of the issue.)
Here is a sample of my data: https://docs.google.com/spreadsheets/d/1kpD88_wEA5YFh5DMMkubsTnFHeNxRQL-njd9Mv-C_lc/edit?usp=sharing
This should return two separate tabs/sheets based on room .
I am obviously not a programmer and do not know Visual Basic or Java or anything. I just know how to google and copy things....amazingly I often get it to work.
Let me know what else you need if you can help.
Try the below code:
'splitSheetIntoTabs' will split your master sheet in to separate sheets of 30 rows each. It will copy only the content not the background colors etc.
'deleteTabsOtherThanMaster' will revert the change done by 'splitSheetIntoTabs'. This function will help to revert the changes done by splitSheetIntoTabs.
function splitSheetIntoTabs() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var header = rows[0];
var contents = rows.slice(1);
var totalRowsPerSheet = 30; // This value will change no of rows per sheet
//below we are chunking the toltal row we have into 30 rows each
var contentRowsPerSheet = contents.map( function(e,i){
return i%totalRowsPerSheet===0 ? contents.slice(i,i+totalRowsPerSheet) : null;
}).filter(function(e){ return e; });
contentRowsPerSheet.forEach(function(e){
//crate new sheet here
var currSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet();
//append the header
currSheet.appendRow(header);
//populate the rows
e.forEach(function(val){
currSheet.appendRow(val);
});
});
}
// use this function revert the sheets create by splitSheetIntoTabs()
function deleteTabsOtherThanMaster() {
var sheetNotToDelete ='Master';
var ss = SpreadsheetApp.getActive();
ss.getSheets().forEach(function(sheet){
if(sheet.getSheetName()!== sheetNotToDelete)
{
ss.deleteSheet(sheet);
}
});
}
I was using Kessy's nice script, but started having trouble when the data became very large, where the script timed out. I started looking for ways to reduce the amount of times the script read/wrote to the spreadsheet (rather than read/write one row at a time) and found this post https://stackoverflow.com/a/42633934
Using this principle and changing the loop in the script to have a loop within the loop helped reduce these calls. This means you can also avoid the second call to append rows (the "else"). My script is a little different to the examples, but basically ends something like:
`for (var i = 1; i < theEmails.length; i++) {
//Ignore blank Emails and sheets created
if (theEmails[i][0] !== "" && !completedSheets.includes(theEmails[i][0])) {
//Set the Sheet name = email address. Index the sheets so they appear last.
var currentSheet = theWorkbook.insertSheet(theEmails[i][0],4+i);
//append the header
//To avoid pasting formulas, we have to paste contents
headerFormat.copyTo(currentSheet.getRange(1,1),{contentsOnly:true});
//Now here find all the rows containing the same email address and append them
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++)
{
if(rows[j][0] == theEmails[i][0]) {
theNewRows[b]=[];//Initial new array
theNewRows[b].push(rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7]);
b++;
}
}var outrng = currentSheet.getRange(2,1,theNewRows.length,8); //Make the output range the same size as the output array
outrng.setValues(theNewRows);
I found a table of ~1000 rows timed out, but with the new script took 6.5 secs. It might not be very neat, as I only dabble in script, but perhaps it helps.
I have done this script that successfully gets each room and creates a new sheet with the corresponding room name and adding all the rows with the same room.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
// This var will contain all the values from column C -> Room
var columnRoom = sheet.getRange("C:C").getValues();
// This var will contain all the rows
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
//Set the first row as the header
var header = rows[0];
//Store the rooms already created
var completedRooms = []
//The last created room
var last = columnRoom[1][0]
for (var i = 1; i < columnRoom.length; i++) {
//Check if the room is already done, if not go in and create the sheet
if(!completedRooms.includes(columnRoom[i][0])) {
//Set the Sheet name = room (except if there is no name, then = No Room)
if (columnRoom[i][0] === "") {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("No Room");
} else {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(columnRoom[i][0]);
}
//append the header
currentSheet.appendRow(header);
currentSheet.appendRow(rows[i]);
completedRooms.push(columnRoom[i][0])
last = columnRoom[i][0]
} else if (last == columnRoom[i][0]) {
// If the room's sheet is created append the row to the sheet
var currentSheet = SpreadsheetApp.getActiveSpreadsheet()
currentSheet.appendRow(rows[i]);
}
}
}
Please test it and don't hesitate to comment for improvements.
I have a FlexTable that contains checkBoxes in the all cells of first column and other data in the other cells. I need to store FlexTable's row when checkBox is true to subsequently put it in document with DocumentApp.create('Doc').getBody().appendTable(storedRows), and I have no idea how to realise this function.
Maybe it impossible when using FlexTable?
Anyway thankyou in advance.
if you need to read the value of a checkBox this checkBox has to have a unique name so you can get it using e.parameter.widgetName. What I usueally do when building in a loop is to generate a name in which I include the row number so I have a direct relation with data.
I rewrote your code with this modification and a few other ones.... please have a look and tell us if you need more details.
// Global variables
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var data = sh.getDataRange().getValues();
var app = UiApp.getActiveApplication();
// Menu additions
function onOpen() {
var menuEntries = [{name: 'Поиск вакансий', functionName: 'ui'}];
ss.addMenu('Отчеты',menuEntries);
}
// UI
function ui() {
var app = UiApp.createApplication().setHeight(400).setWidth(800).setTitle('Поиск вакансий');
var panel = app.add(app.createHorizontalPanel());
var input = panel.createTextBox().setName('input').setId('input').setFocus(true);
var button = panel.createButton('Сформировать документ');
panel.add(input).add(button);
var handler = app.createServerHandler('search');
input.addChangeHandler(handler);
handler.addCallbackElement(input);
var click = app.createServerHandler('click');
button.addClickHandler(click);
click.addCallbackElement(button);// this is useless, the button has no data to transmit ?
var table = app.createFlexTable().setId('table').setWidth(785).setBorderWidth(1).setCellPadding(1)
var tpanel = app.createScrollPanel(table).setPixelSize(800, 383).setAlwaysShowScrollBars(true);
table
.setWidget(0,0,app.createCheckBox().setValue(true).setEnabled(false))
.setWidget(0,1,app.createLabel('Вакансия'))
.setWidget(0,2,app.createLabel('График'))
.setWidget(0,3,app.createLabel('Время'))
.setWidget(0,4,app.createLabel('Условия'))
.setWidget(0,5,app.createLabel('Зарплата'))
.setWidget(0,6,app.createLabel('Оплата'))
.setWidget(0,7,app.createLabel('Организация'))
.setWidget(0,8,app.createLabel('Телефон'));
app.add(tpanel);
ss.show(app);
}
// Search
function search(e) {
var table = app.getElementById('table');
var query = e.parameter.input.toLowerCase();
var hidden = app.createHidden().setId('hidden').setName('hidden');
app.add(hidden);// set a name too but I don't use it right now
var check = app.createServerHandler('check');
check.addCallbackElement(table);
var r = 1;
for (var row = 0; row < data.length; row++) {
if(data[row].toString().toLowerCase().match(query) == query && query!= ''){ // you don't need to check each cell, you can use match on the stringified row instead.
table.setWidget(r,0,app.createCheckBox().addValueChangeHandler(check).setName('check'+row));// create the checkBow once every row if condition is true and give it a name
for (var c = 1; c < data[row].length; ++c) {
table.setText(r,c,data[row][c].toString());
}
++r
continue;
}
}
return app;
}
// Storing checked rows
function check(e) {
var checkedArray = [];
for(var n=0; n < data.length;++n){
Logger.log('check'+n+' = '+e.parameter['check'+n]);// shows also 'undefined' for items not found by input query, that's normal
if(e.parameter['check'+n]=='true'){
checkedArray.push(data[n]);
}
}
Logger.log('checkedArray = '+checkedArray);// see results
}
function click(e) {
// no action right now
}