Placing checkboxes in Google Sheets using Apps Script - checkbox

I know that checkbox is a relatively new feature in Google Sheets, so I'm trying to find a way to automatically create checkboxes in cells.
So far, I haven't found a reference regarding this in Google Apps Script documentation.
Currently I'm doing it manually, but any suggestion using script will be much appreciated.

UPDATE(April 2019)
You can now directly insertCheckboxes(or removeCheckboxes) on a Range or RangeList without any workarounds. You can also change the checked value/unchecked value using alternate method signatures found in the documentation.
Snippet:
SpreadsheetApp.getActive()
.getRange('Sheet2!A2:A10')
.insertCheckboxes();

I'm not sure when they did it, but they've added this now.
Use class DataValidationBuilder's requireCheckbox() method. Example:
function setCheckboxes() {
// Assumes there's only one sheet
var sheet = SpreadsheetApp.getActiveSheet();
// This represents ALL the data
var dataRange = sheet.getDataRange();
/* Get checkbox range from sheet data range. Assumes checkboxes are on the
left-most column
*/
var dataRangeRow = dataRange.getRow();
var dataRangeColumn = dataRange.getColumn();
var dataRangeLastRow = dataRange.getLastRow();
var checkboxRange = sheet.getRange(
dataRangeRow,
dataRangeColumn,
dataRangeLastRow
);
var enforceCheckbox = SpreadsheetApp.newDataValidation();
enforceCheckbox.requireCheckbox();
enforceCheckbox.setAllowInvalid(false);
enforceCheckbox.build();
checkboxRange.setDataValidation(enforceCheckbox);
}

You want to create the checkbox in the cells of spreadsheet using the scripts. If my understanding is correct, how about this workaround? Unfortunately, the Class SpreadsheetApp has no methods for creating the checkbox yet. (When such methods are tried to be used, the error occurs.) So I would like to propose to create it using Sheets API.
When I saw ConditionType of dataValidation, the document of BOOLEAN says
The cell's value must be TRUE/FALSE or in the list of condition values. Supported by data validation. Renders as a cell checkbox. ...
From this, I could understand how to create the checkbox using Sheets API. The following script is a sample script. This creates 6 checkboxes to "A1:C3". When you use this script, please enable Sheets API at Advanced Google Services and API console as follows.
Enable Sheets API v4 at Advanced Google Services
On script editor
Resources -> Advanced Google Services
Turn on Google Sheets API v4
Enable Sheets API v4 at API console
On script editor
Resources -> Cloud Platform project
View API console
At Getting started, click Enable APIs and get credentials like keys.
At left side, click Library.
At Search for APIs & services, input "sheets". And click Google Sheets API.
Click Enable button.
If API has already been enabled, please don't turn off.
If now you are opening the script editor with the script for using Sheets API, you can enable Sheets API for the project by accessing this URL https://console.cloud.google.com/apis/library/sheets.googleapis.com/
Sample script :
In this sample script, the checkboxes are created to "A1:C3" of Sheet1. Please use this script as the container-bound script.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetId = ss.getSheetByName("Sheet1").getSheetId();
var resource = {"requests": [
{
"repeatCell": {
"cell": {"dataValidation": {"condition": {"type": "BOOLEAN"}}},
"range": {"sheetId": sheetId, "startRowIndex": 0, "endRowIndex": 3, "startColumnIndex": 0, "endColumnIndex": 3},
"fields": "dataValidation"
}
},
{
"updateCells": {
"rows": [
{"values": [{"userEnteredValue": {"boolValue": true}}, {"userEnteredValue": {"boolValue": false}}, {"userEnteredValue": {"boolValue": false}}]},
{"values": [{"userEnteredValue": {"boolValue": true}}, {"userEnteredValue": {"boolValue": true}}, {"userEnteredValue": {"boolValue": false}}]},
{"values": [{"userEnteredValue": {"boolValue": true}}, {"userEnteredValue": {"boolValue": true}}, {"userEnteredValue": {"boolValue": true}}]}
],
"start": {"rowIndex": 0, "columnIndex": 0, "sheetId": sheetId},
"fields": "userEnteredValue"
}
}
]};
Sheets.Spreadsheets.batchUpdate(resource, ss.getId());
Flow :
dataValidation is set using repeatCell.
boolValue is set using updateCells.
Result :
Note :
This is a simple sample script. So please modify this for your environment.
When the methods of the Class SpreadsheetApp for creating the checkbox can be used, I think that the following sample script might be able to be used.
Script for Class SpreadsheetApp
At June 22, 2018, this script returns an error of the server error yet.
var rule = SpreadsheetApp.newDataValidation().withCriteria(SpreadsheetApp.DataValidationCriteria.CHECKBOX, ["TRUE", "FALSE"]).build();
SpreadsheetApp.getActiveSheet().getRange("A1").setDataValidation(rule);
References :
ConditionType
Advanced Google Services
Sheets API v4
If I misunderstand your question, I'm sorry.

The checkbox is the recently added Data Validation criterion. Interestingly enough, when I attempt to call the 'getDataValidation()' method on the range that contains checkboxes, the following error is thrown:
var rule = range.getDataValidation();
We're sorry, a server error occurred. Please wait a bit and try again.
In the meantime, you can work around this by placing a single checkbox somewhere in your sheet and copying its Data Validation to the new range. For example, if "A1" is the cell containing the checkbox and the target range consists of a single column with 3 rows:
var range = sheet.getRange("A1"); //checkbox template cell
var targetRange = sheet.getRange(rowIdex, colIndex, numOfRows, numOfCols);
range.copyTo(col, SpreadsheetApp.CopyPasteType.PASTE_DATA_VALIDATION);
var values = [["true"], ["false"], ["false"]];
targetRange.setValues(values);

Short answer
Add the checkbox from the Google Sheets UI, then use one of the copyTo
methods of Class Range.
Explanation
The Google Apps Script Spreadsheet service doesn't include a methods for everything that could be done through the Google Sheets user interface. This is the case of the Insert > Checkbox which is a pretty new feature.
Even the Record macro feature can't do this. The following was recorded one momento ago
/** #OnlyCurrentDoc */
function InsertCheckbox() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A1').activate();
/*
* Added to show the missing Insert > Checkbox step
*/
spreadsheet.getRange('B1').activate();
spreadsheet.getRange('A1').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
};
NOTE: If you don't want to pass all the cell properties (borders, formulas, background, etc. instead of SpreadsheetApp.CopyPasteType.PASTE_NORMAL use SpreadsheetApp.CopyPasteType.PASTE_DATA_VALIDATION.
Related Q on Stack Overflow
Google Sheets: Add a CheckBox with a script

function onEdit() {
var cell = SpreadsheetApp.getActive().getRange('A1');
var array =['☐','☑'];
// var rule = SpreadsheetApp.newDataValidation().requireValueInList(['☐','☑']).build();
var rule = SpreadsheetApp.newDataValidation().requireValueInList(array, false).build()
cell.setDataValidation(rule);
var valor = array[1];
// Logger.log(valor);
if(cell.getValue() == valor){
cell.offset(0, 1).setValue("Aprobado");
} else{
cell.offset(0, 1).setValue("Reprobado");
}
}

Easy:
//There are two ways, using Range or using current cell or sheet or similar
//First is using current cell
function VoF() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getCurrentCell().offset(1, 0, 499, 1).setDataValidation(SpreadsheetApp.newDataValidation()
.setAllowInvalid(true)
.setHelpText('TRUE or FALSE')
.requireCheckbox() //if you customize this is possible that you dont get the boxes and the verification data could fail,so keep them standar with TRUE and FALSE
.build());
};
//Second is using Spreedsheet ranges
function myFunction() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('G1:G11').activate();
spreadsheet.getRange('G1:G11').setDataValidation(SpreadsheetApp.newDataValidation()
.setAllowInvalid(false)
.requireCheckbox()
.build());
};

I agree that you have to workaround to create a checkbox. Another way maybe is to create a dropdown list.
function myFunction() {
var cell = SpreadsheetApp.getActive().getRange('A1');
var rule = SpreadsheetApp.newDataValidation().requireValueInList(['☐','☑']).build();
cell.setDataValidation(rule);
}

Related

SuiteScript 2.0: How do you load a dataset and then add conditions?

The situation:
I am trying to load a dataset and then add additional criteria (filters) to the dataset based off users selected fields. The whole dev is a "Custom Report" build using a suitlete that has some fields the user can populate to choose "dynamic filters". When they click on the generate button I add the criteria/filters to a search and dataset and then join the results and display them.
The issue is that while I am able to add filters to the search after I load it no matter what I try I can't seem to add filters to the Dataset.
This code gets the dataset Data:
var datasetData = datasetLib.load({ id: datasetId });
resultSet.pageRanges.forEach(function (pageRange) {
// Fetch the results on the current page
var myPage = resultSet.fetch({ index: pageRange.index });
res.data = res.data.concat(myPage.data.results);
if (res.columns.length < 1) {
var columns = JSON.parse(myPage.pagedData.queryDefinition).columns;
for (var i = 0; i < columns.length; i++) {
res.columns.push(columns[i].label);
}
}
});
I attempted many different iterations to create the condition... here is one:
dataset.createCondition({
column: datasetData.columns[0], // I loaded the dataset and use it to reference the column
operator: query.Operator.ANY_OF,
values: params.customer.split(',')
})
Now the above code DOSE create a condition but when I attempt to add it into the dataset's current conditions I receive errors.I am attempting to push it into the child parameter of the parent criteria.
Please ask if you need more info...
If using a workbook is fine then I would suggest you to load the workbook using your above dataset using the query module and then use the above createCondition to add the condition to the loaded query dynamically.
var myLoadedQuery = query.load({
id: 'custworkbook237'
});
var mySalesRepJoin = myLoadedQuery.autoJoin({
fieldId: 'salesrep'
});
var thirdCondition = mySalesRepJoin.createCondition({
fieldId: 'email,
operator: query.Operator.START_WITH_NOT,
values: 'foo'
});
I would also urge to ensure the joins are accurately represented by looking at the Records catalog via Setup>Records Catalog. Hope this helps.

DotNet Core Azure Search SDK - filtering results

We are trying to implement Filter functionality into Azure (Cognitive) Search. I was hoping to find some nice SDK methods that hide all the ugly parts, but so far the only example I found looks like this (source):
SearchParameters parameters = new SearchParameters()
{
Filter = String.Format("groupIds/any(p:search.in(p, '{0}'))", string.Join(",", groups.Select(g => g.ToString()))),
Select = new[] { "application essays" }
};
I was wondering, whether I am missing some docs. Or maybe it is on the roadmap?
Check out our new Azure.Search.Documents SDK we released last month. It does have OData filter helps as you can find here:
int stars = 4;
SearchOptions options = new SearchOptions
{
// Filter to only Rating greater than or equal our preference
Filter = SearchFilter.Create($"Rating ge {stars}"),
Size = 5, // Take only 5 results
OrderBy = { "Rating desc" } // Sort by Rating from high to low
};
It'll escape string parameters correctly. The OData $filter syntax still requires raw input, but the type helpers in the formattable string should make your situation easier: you don't have to worry about escaping values yourself.

Get all instances of character present in cell on row in specific range

I have a Spreadsheed that looks like this:
I need to create a map from these values. But I also need each child's parents. The map will be uploaded to a database where the children parent ID will be added based on the parent name from the spreadsheet.
=ArrayFormula("{'title':"""&Sheet1!A2:A4&""", 'row': "&ROW(Sheet1!A2:A4)&" 'parents': ['?','?'], 'children': false,},")
Using ROW(Sheet1!B3:B48) I can get the row number ... so I was thinking of using it together with A to scan that row for x and where that's found, get the column then using $column1 to get the parent's name. But this is my first time working with spreadsheet functions ...
Edit*
{'title': 'Child3', parents:['Parent1', 'Parent2', 'Parent3']}
You can accomplish this via a custom function created in Google Apps Script. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project:
function MAPPING(input) {
const parents = input.shift();
const output = input.map(row => {
let childParents = [];
for (let i = 1; i < row.length; i++) {
if (row[i] == "x") childParents.push(parents[i]);
}
return [JSON.stringify({
title: row[0],
parents: childParents
})]
})
return output;
}
Now, if you go back to your spreadsheet, you can use this function as if you were using a regular sheets formula. You just have to provide the appropriate range as an argument (in this case A1:D4), as you can see here:
Reference:
Custom Functions in Google Sheets
try:
=INDEX(FLATTEN(IF(B2:D="",,B1:D1&" "&A2:A5)))

Populate Google Apps flexTable with filtered Spreadsheet Data and returning changes to spreadsheet

I am writing an application that pulls task data (for a task management system) from rows in a Google Spreadsheet with conditions matching a query. I then send this data into a flexTable next to a checkbox with an onClickHandler as well as a textbox to enter in hours data. Below is the simplest example of how I have achieved this:
\\establish handler for checkboxes that are generated in flexTable
var updateHandler = app.createServerClickHandler('updateTasksfunc');
updateHandler.addCallbackElement(taskTable);
\\search Google Spreadsheet for rows matching condition
for(var i=1; i< taskData.length; i++){
if (taskData [i][1] !=employee){
continue;
}
\\look up if task is already completed, and set checkbox value to true/false
var complete = taskData[i][8].toString();
if(complete==="true"){var checkbox=true;}else{var checkbox=false;}
\\populate flexTable with values from spreadsheet query
taskTable.setWidget(i,0,app.createTextBox().setName('hours'+i).setWidth('50').setId("hour"+i)).setWidget(i,1,app.createCheckBox().setValue(checkbox).setName('complete'+i).addClickHandler(updateHandler)).setWidget(i,2,app.createLabel(taskData[i][2]).setWidth('250')).setWidget(i,3,app.createLabel(taskData[i][3]).setWidth('250')).setWidget(i,4,app.createLabel(taskData[i][4])).setWidget(i,5,app.createLabel(taskData[i][5])).setWidget(i,6,app.createLabel(taskData[i][6])).setWidget(i,7,app.createLabel(taskData[i][7])).setWidget(i,8,app.createTextBox().setVisible(false).setName("dataRow"+i).setValue(i))
}
\edits to doGet()
var numberOfItems = app.createTextBox().setName('rows').setValue(i).setVisible(false);
var rows1 = i+1;
taskTable.setWidget(rows1,0,numberOfItems)
I have stored the spreadsheet data row in the invisible textbox in column 8 of the flex table that I intend to use in the clickHandler function to update the Google Spreadsheet.
I have successfully activated a click handler that runs when a checkbox is clicked; however, I have not yet successfully figured out a way to grab data from the flexTable that I can use to update the Spreadsheet.
//Functional code below
function updateTasksfunc(e){
var app = UiApp.getActiveApplication();
var taskSS = SpreadsheetApp.openById('SPREADSHEETID');
var taskSH = taskSS.getSheets()[0];
var taskData = taskSS.getDataRange().getValues();
var results = [];// an array to collect results
var numberOfItems = e.parameter.rows;
app.add(app.createLabel(numberOfItems));
for(var n=0;n<numberOfItems;n++){
results.push([e.parameter['check'+n]]);// each element is an array of 2 values
}
for(var i=1;i<results.length;i++){
var check = results[i][0];
var row = i+1;
taskSH.getRange(row,9,1,1).setValue(check);
}
return app;
}
I am getting a generic error: cannot find function getRowCount() from generic.
I assume that this means that I am not properly calling my flexTable.
Any thoughts??
flextables are not working like spreadsheets, you can't get widgets values directly as part of the table. You have to get every textBox and checkBox separately using e.parameter.Name.
You can do that easily in a loop but you'll have to know the number of textBoxes to be able to rebuild the names just as you did to create them in the doGet function.
I would suggest to store this number in a tag on one of your widget or in a separate hidden widget (hidden class or invisible textBox). Then you will be able to get the values like this :
...
var results = [];// an array to collect results
var numberOfItems = Number(e.parameter.itemNumbers); // this is the var you are missing right now and need to add. This is the solution of a separate hidden widget
for(var n=0;n<numberOfItems;n++){
results.push([e.parameter['hours'+n],e.parameter['complete'+n]]);// each element is an array of 2 values
}
... // from here you will have an 2D array with all your values and you can continue like you did=.

Is it possible to create a public database (spreadsheet) search with Google Scripts?

I'm trying to create a website where users can come and look for a set of resources, something like a portal, or a database like JSTOR. I am using Weebly; this website will eventually be turned over to someone who does not know computers well, so I'm trying to keep things simple (and free, where doable).
My thought was to use Google Spreadsheets/Forms to handle the input and storage of the data for each individual resources (Title, Author, Type, Topic, Country, etc.), and then find some some method of creating a search function that could placed on the website. Any user could arrive at the site, put in whatever criteria they want to look for, and any resources in the database would be listed out for the user to further investigate. Users would not be adding data to the spreadsheets; only querying it for data.
My first question is such a script/arrangement possible and can it be embedded into a website page? My second question is what would the best approach be?
Yes this is certainly possible, but can achieved in a variety of ways.
One approach you could take with this is to retrieve all the data from the spreadsheet as JSON format and add it to the DOM as a HTML table. Then you can use a nice plugin like dataTables which has a pretty good native search function. I'll give a basic example below.
To retrieve the data you can use Googles spreadsheet JSON API. A basic example is below.
<script src="http://spreadsheets.google.com/feeds/cells/*ID*/*WS*/public/values?alt=json-in-script&callback=*FN*"></script>
Where ID is the spreadsheet's long ID.
Where WS is the worksheet number e.g. 1,2,3 etc.
Where FN is the function you want to call. In my below function i use importGSS
Then I've written the below script that adds the data to a HTML table. It first adds the first row to a <thead> section and then adds the rest to the <tbody> section.
function cellEntries(json, dest) {
var table = document.createElement('table');
var thead = document.createElement('thead');
var tbody = document.createElement('tbody');
var thr;
var tr;
var entries = json.feed.entry;
var cols = json.feed.gs$colCount.$t;
for (var i=0; i <cols; i++) {
var entry = json.feed.entry[i];
if (entry.gs$cell.col == '1') {
if (thr != null) {
tbody.appendChild(thr);
}
thr = document.createElement('tr');
}
var th = document.createElement('th');
th.appendChild(document.createTextNode(entry.content.$t));
thr.appendChild(th);
}
for (var i=cols; i < json.feed.entry.length; i++) {
var entry = json.feed.entry[i];
if (entry.gs$cell.col == '1') {
if (tr != null) {
tbody.appendChild(tr);
}
tr = document.createElement('tr');
}
var td = document.createElement('td');
td.appendChild(document.createTextNode(entry.content.$t));
tr.appendChild(td);
}
$(thead).append(thr);
$(tbody).append(tr);
$(table).append(thead);
$(table).append(tbody);
$(dest).append(table);
$(dest + ' table').dataTable();
}
You can then call back the function with ... where #Destination is the <div> you want to add the HTML table to.
function importGSS(json){
cellEntries(json, '#Destination');
};
Once all completed you'll see something like the below screenshot, the top the final results and the bottom the original spreadsheet. I've edited out some information. I hope this has been of some help.

Resources