I have an Add-on I'm updating for Sheets. I want to store information each time the user runs one of two functions, so I've created a function to push than info to Document Properties. Ultimately I want to send that data to a sheet at my end once a certain number of values have been collected.
The problem I'm having is that when I run a test function to Log the data contained, it only shows me the most recent data; I'm not sure I'm adding to existing data or replacing it. The data pairs should include the user's email address and the name of the sheets tab created by one of two functions that call this storeStats function.
In short:
*Do I need to declare the name of the Property Store before adding data to it?
*How do I add more data without deleting the old?
*How can I check how much data is stored? I'm thinking along the lines of array.length, but not sure if that works in Properties
*I'm assuming I need to use the parse command to retrieve it and send to the sheet at my end. That may wind up in a separate question later, but any ideas are appreciated.
function storeStats(sheetTitle) {
var docProps = PropertiesService.getDocumentProperties();
var userID = Session.getActiveUser().getEmail();
var thisData = {user:userID, sheet:sheetTitle};
var thisDataStr = JSON.stringify(thisData);
var useData = "USEDATA"; //name of the properties store maybe
docProps.setProperties(useData,thisDataStr);
Logger.log(useData.length);
//send when enough values are present
//use parse to extract and send?
// /*if(/*see how many values are in the data set*/) {
//parse values from value set array
//send the whole batch to the range on the collection sheet
//} */
}
No errors are created thus far, but this only returns one email address and one sheet name rather than all values send from previous function calls.
docProps.setProperties(useData,thisDataStr); is not adding data to "USEDATA" if you want to add or append data to it you need to do something like this:
docprops.setProperty('USEDATA', docprops.getProperty('USEDATA') + thisDataStr);
Example:
function propertiesTest() {
var ps=PropertiesService.getScriptProperties();
ps.setProperty('test','');
for(var i=0;i<10;i++) {
ps.setProperty('test',ps.getProperty('test') + '\n' + i)
}
Logger.log(ps.getProperty('test'));
}
Related
During storing an object to my firebase, I am expecting the structure as image below, but what I get was a generated running number as a key. This is my code to store an object to firebase
var location = [];
location.push({
ms_jhr_1 : {
name: value
},
...
});
const a = firebase.database().ref('Food/'+id);
a.set(location);
How do I keep my structure without generate the running number?
The problem is you are using an array to store your data and then setting that array in firebase. To get the expected result you have to modify your code a little bit.
Here use this and remove other code
const a = firebase.database().ref('Food/'+id);
a.set(ms_jhr_1);
So you just need to pass the object you want to store under that id and not the whole array.
Note:- If you want to store multiple entries under one id then you have to push all those entries in an Object and not in array.
So it will look something like this
var location = {};
Now use for loop to insert all your data into this object (Remember, you are adding objects inside an object). You don't need array. Because in firebase data is stored in JSON tree format.
Hope it helps.
I should start with letting you know that I'm an extreme novice in JS. My background is almost solely in SQL/VBA. So, any tips you could provide would be greatly appreciated, whether it's in coding or in Stack Overflow etiquette.
Problem Background:
I've got a Script that I use for sending outbound e-mails from Google Form responses, hosted within a Google Sheet and set to OnFormResponse(). Typically, I'm only asked to send back specific bits of information from the form responses within a HTML template e-mail. However, the business case I have now is that I need to look up the values from another sheet, where an adjacent column's value matches a form response value. With the value that's matched, I need to set the value of a specific column/row (F:F) within the Form Response sheet with it.
Example:
Here's a simplified version of what the Form Responses sheet looks like, along with the formula that I would typically use:
Here's what the other tab, 'Unique Databases!', looks like:
So, my understanding of JavaScript arrays is that on the Form Responses Sheet, I would load all columns (A:E in this example) into a variable, and get the values. Then, get columns A:B of 'Unique Databases!', which loads those values into another array. If that is accurate, how do you compare the index of 1 array against the index of another, and return an adjacent match?
Thanks!
You can try this function:
function dbmanager(dbname) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Unique Databases!');
var rg=sh.getDataRange();
var vA=rg.getValues();
var r='';
for(var i=0;i<vA.length;i++){
if(dbname==vA[i][0]){
r=vA[i][1];
break;
}
}
return r;
}
I'm not sure you need to use a apps script to make a comparison like this. Perhaps a much faster way would be to use a query in the sheet. Something like this maybe:
=QUERY(Sheet1!A1:B3,"SELECT B WHERE A ='"&E2&"'",0)
The first part of the query is looking up the unique databases data. The second part selects column B in the unique databases data where column A is equal to the data base name in the form responses data. Note this query goes in column F of the responses data.
Another alternative using code might be something like this. with the code below running.
function getFormData(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet2');
var getRange = sheet.getRange('E2:E');
var data = getRange.getValues();
var lookup = getLookupData();
data.forEach(function(item,index){
sheet.getRange(index + 2 , 6).setValue(lookup[item])
})
}
function getLookupData() {
var obj = {};
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange('A2:B6');
var data = range.getValues();
data.forEach(function(item){
obj[item[0]] = item[1];
})
Logger.log(obj)
return obj;
}
I have a Service Catalog item in ServiceNow (Geneva release) that personnel use to request access to additional ServiceNow groups. A list collector displays the all of the groups available. We want to limit it and disallow a few choices from appearing. When you build a List Collector variable, you can specify a List table, but you cannot construct a filter on it.
Is it possible to use a Catalog Client Script to check each item being loaded into the List Collector and skip it, perhaps by checking sysID or another value? I assume a glide record call is being done somewhere in order to populate the list collector, but I'm unsure how to modify it or interrupt it.
For this example, the List Collector is named 'bucket'.
I can probably try to start with:
var lcFilter = g_form.getControl('bucket')
There is probably a way to manipulate the contents from that object. I just need help figuring out what part of the object I need to manipulate. We currently use a similar function to rename the headers of the List Collector like this:
var headers = g_form.getControl('bucket').parentElement.querySelectorAll('.col-xs-4');
headers[0].childNodes[0].firstChild.data = 'All Available Groups';
headers[1].childNodes[0]firstChild.data = 'Groups you wish to add';
You may want to try a solution that allows you to directly apply a filter to your list collector that I've used from http://www.servicenowguru.com/scripting/client-scripts-scripting/changing-filter-list-collector-variable-client-script/
The code below is a copy and paste from there, so I don't take too much credit, but you want to put your list collector name here
var collectorName = 'bucket';
Then an encoded query here to filter it on.
var filterString = 'group_nameNOT LIKEApproval^active=true'
This would be in a Catalog Client Script for onLoad
function onLoad() {
//Apply a default filter to the list collector variable
var collectorName = 'configuration_items';
var filterString = 'name!=NULL^sys_class_nameANYTHING';
//Hide the list collector until we've set the filter
g_form.setDisplay(collectorName, false);
setCollectorFilter();
function setCollectorFilter(){
//Test if the g_filter property is defined on our list collector.
//If it hasn't rendered yet, wait 100ms and try again.
if(typeof(window[collectorName + 'g_filter']) == 'undefined'){
setTimeout(setCollectorFilter, 100);
return;
}
//Find and hide the filter elements (optional)
//Simple method for items with only one list collector
//$('ep').select('.row')[0].hide();
//Advanced method for items with more than one list collector (more prone to upgrade failure)
//var el = $('container_' + g_form.getControl(collectorName).id).select('div.row')[0].hide();
//Reset the filter query
window[collectorName + 'g_filter'].reset();
window[collectorName + 'g_filter'].setQuery(filterString);
window[collectorName + 'acRequest'](null);
//Redisplay the list collector variable
g_form.setDisplay(collectorName, true);
}
}
I have two tables A & B, in table B I have a column of type pointer, each field points to a class in table A.
I need to know how to query table B based on the value of a particular field in the pointer column.
So for example, I have 4 objects (records) in table B, and in the pointer column I have pointer1, pointer2, pointer3 and pointer4. So if I want to carry out a query on table B and I want to extract the record with the field value of pointer3 how do I do this in javascript?
Based on your comment I would suggest storing both object IDs for each row. You could use Parse.when(recordA.save(), recordB.save()).then(...) to wait until both finished if you needed to provide feedback in the UI.
An alternative is to just store the object ID for the table B record and in the success handler you'll get back the updated record that'll include the pointer where you can then execute another save. This is slow as it will do two saves in sequence instead of kicking both off at once.
So after a long time searching and looking around and asking questions I figured it out myself in the end. I needed to pass in an object in the query.equalTo method like so:
query.find({
success: function(results) {
var detailsObject = results[0];
detailsObject.set("uuid", uuid);
detailsObject.set("proximity", prox);
detailsObject.set("campaignId", campaignId);
detailsObject.set("location", location);
detailsObject.set("sub_location", subLoc);
detailsObject.save(null, {
success: function(detailsObject) {
var content = Parse.Object.extend("Content");
var contentQuery = new Parse.Query(content);
var id = detailsObject.id;
contentQuery.equalTo("descId", detailsObject);
So "descId" is the pointer column in my content table and rather than trying to query the value in the field (object id of details object), I needed to pass the details object into the query.equalTo
I hope this is of benefit to someone else.
I'm trying to build a history list of clicked clicked page elements and store that list into HTML local storage, to be later displayed back to the user. The main pre-requisite is that the list cannot contain duplicates, so for example if the user clicks on item A and then on item B and again back on item A, only A and B are recorded. The third click is not recorded because it is not unique.
I'm also using persist.js.
I noticed that I am able to name the storage and give it a key and both are stored together in the real key of the localstorage thus: myStorageName>myKeyand my value is whatever I put there.
Here's the thing. I know you can store stringyfied JSON there but my list is built up from simple javascript variables one at at time.
I know what to do for the first click:
myStorageName.set(myKey, myCurrentElementId); // myCurrentElementId = this.id
now on the second click this is where I'm beginning to getting stuck. There is the original variable value already stored, now I want to append the new variable value. Assume that I can get the value from the store like this:
var dataExtract = myStorageName.get(myKey);
myObject = JSON.parse(dataExtract);
But how do I then turn this into a JSONstring -able thing (sorry I don't even know what it should be) that contains only a list of unique values. Does this make any sense to anyone?
First of all, you don't want to keep writing to/from localStorage everytime a link is clicked, because this'll slow down your page. Keep an updated Array populated with the element ids, then write to localStorage before the user navigates away from the page (by binding to the window's onbeforeunload event, for instance).
First:
var clickedLinks = []; // this Array will hold the ids of the clicked links
function uniqueClick(id){
return !~clickedLinks.indexOf(id); // this tests whether the id is already in the Array
};
In your click handler:
if(uniqueClick(this.id)){
clickedLinks.push(this.id); // append the new element id to the Array
}
Bind to window.onunload to save the Array before the user navigates from the page:
window.onunload = function(){
localStorage.setItem('clickedLinks',JSON.stringify(clickedLinks)); // stringify the Array and save to localStorage
}
To retrieve clickedLinks on subsequent page visit:
// convert the String back to an Array; try/catch used here in case the value in localStorage is modified and unable to be parsed, in which case clickedLinks will be initialized to an empty Array
try{
var clickedLinks = JSON.parse(localStorage.getItem('clickedLinks')) || [];
}catch(e){
var clickedLinks = [];
}
You may want to replace the first line (var clickedLinks = [];) with this last bit of code, as it will initialize the Array if it doesn't exist.
UPDATE:
IE8 does not support Array.indexOf. Alternatives might be:
use jQuery's $.inArray by replacing !~clickedLinks.indexOf(id); with !~$.inArray(id, clickedLinks);
Detect whether Array.prototype.indexOf is supported. If not, shim it with the code provided on this page.
Your model has an error. At the first time, you save a primitive value. Then, you want to "append" another value to it. Seems like you actually want to use an object:
var myObj = localStorage.getItem("myName");
if(myObj) myObj = JSON.parse(myObj); //Variable exists
else myObj = {}; //Elsem create a new object
function appendNewValue(name, value){
myObj[name] = value;
localStorage.setItem("myName", JSON.stringify(myObj));
/* Saves data immediately. Instead of saving every time, you can
also add this persistence feature to the `(before)unload` handler. */
}
I suggest to define in your code this:
localStorage.set= function(key,val)
{
localStorage.setItem(JSON.stringify(val));
}
localStorage.get = function(key,defval)
{
var val = localStorage.getItem(key);
if( typeof val == "undefined" ) return defval;
return JSON.parse(val);
}
and use them instead of get/setItem. They will give you ready to use JS values that you can use in the way you need.