Submitting a form through google scripts - javascript

I need to submit a form in a google script but get this error:
TypeError: Cannot call method "withItemResponse" of undefined
According to the link below, this is how it should be set up https://developers.google.com/apps-script/reference/forms/form#createResponse()
Code:
//Submit form
var formID = row[24];
var form = FormApp.openById(formID);
Logger.log(form.getId()); //returns correct ID
form.createResponse() ;
form.FormResponse.withItemResponse('Core Teachers', logSummary);
//form has only two questions, a short text and a paragraph text
form.FormResponse.submit();

form.createResponse() returns a FormResponse, which you need to assign to a variable.
also, withItemResponse() expects an object of type ItemResponse. I am not familiar with google forms, but maybe this gets you in the right direction:
var formID = row[24];
var form = FormApp.openById(formID);
var formResponse = form.createResponse();
// get items of form and loop through
var items = form.getItems();
for (index = 0; index < a.length; ++index) {
var item = items[index]
// Cast the generic item to the text-item class. You will likely have to adjust this part. You can find the item classes in the documentation. https://developers.google.com/apps-script/reference/forms/item-type.
if (item.getType() == 'TEXT') {
var textItem = item.asTextItem();
var itemresponse = textItem.createResponse('Core Teachers');
formResponse.withItemResponse(itemresponse);
}
}
formResponse.submit();
Generally, when the documentation of a method lists as parameter type something else than primitive types like String or Boolean you need to create or aquire an object of that type, like I did with createResponse. You need to familiarize yourself with these and other principles because the GoogleAppsScript documentation assumes knowledge of them.

Related

jQuery - Take array of objects, then sort based on a dataset

I have a game I created that will add questions missed to my array missedArr, in my JSFiddle example, I have 2 buttons (both set to be wrong answers). After clicking these, it seems to have stored both clicks correctly, however I want give a readout at the end of the game to show my user what parts they did well on vs ones they did poorly on.
To do this, I created a function called determineScorecard which should serve to create a dict of my missedArr, however, when it goes to trigger I get undefined.
The dSc function should be sorting across the data-category set on the html buttons, then I want to console.log only the ones that were missed in category-2
function determineScorecard (){
//build dictionary for missed questions
var sortedMissed = {};
for( var i = 0, max = missedArr.length; i < max ; i++ ){
if( sortedMissed[missedArr[i].category] == undefined ){
sortedMissed[missedArr[i].category] = [];
}
sortedMissed[missedArr[i].category].push(missedArr[i]);
}
console.log(sortedMissed["2"]);
}
I can't seem to get this to split up correctly.
I think this is what you're trying to do:
var incorrect = [];
$('.answer').click(function(){
var data = $(this).data(),
correct = data.correct;
if(!data.correct){
incorrect.push(data);
determineScorecard();
}
});
function determineScorecard(){
var missed = {};
for(var i = 0, max = incorrect.length; i < max ; i++){
var question = incorrect[i];
if(!missed[question.category]){
missed[question.category] = [];
}
missed[question.category].push(question);
}
console.log(missed);
}
DEMO
However, I don't see how this can produce what you're expecting. The scorecard logic makes zero sense to me (with the code that's been provided). Can you post a complete example with the questions so we can see the entire flow?
Maybe you want something like this?
var missedArr = [];
$('.answer').click(function(){
var da=$(this).data();
if (da.correct) return false;
missedArr.push(da);
determineScorecard();
});
function determineScorecard (){
var sortedMissed = {};
$.each(missedArr,(i,da)=>{
if( sortedMissed[da.category] === undefined ){
sortedMissed[da.category] = [];
}
sortedMissed[da.category].push(da);
})
console.log(JSON.stringify(sortedMissed));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button class="btn btn-danger answer" data-category="2" data-question="2" data-value="300" data-correct="false">Google</button>
<button class="btn btn-danger answer" data-category="3" data-question="2" data-value="300" data-correct="false">Full-circle reporting</button>
In my solution I have not stored the whole buttonDOM elements but only their data()-objects into the sortedMissed array since these contain all the information about the question and button pressed. But if you don't like it, feel free to change that back again ...
Referencing your JSFiddle: The missedArr array contains DOM elements. To get the category, value, etc of that element you'll need to access the dataset property of the element.
missedArr[i].category // doesn't exist
missedArr[i].dataset.category // should exist
Updated for jQuery, which uses the .data() method:
missedArr[i].data('category') // should exist
When wrapping 'this' in the jQuery function like $(this), the element becomes a jQuery object, so to access the dataset use the .data() method rather than trying to access the properties directly like you would with vanilla JS:
// lines 22 - 24 of your JSFiddle
if( sortedMissed[missedArr[i].category] == undefined ){
sortedMissed[missedArr[i].category] = [];
}

textContent Vs. innerText Cross Browser solution

I've been having a hard time with cross browser compatibility and scrapping the dom.
I've added data analytics tracking to ecommerce transactions in order to grab the product and transaction amount for each purchase.
Initially I was using document.querySelectorAll('#someId')[0].textContent to get the product name and that was working fine for every browser except internet explorer.
It took some time to figure out that it was the .textContent part that was causing ie problems.
Yesterday I changed .textContent to .innerText. From looking inside analytics it seems that the issue has been resolved for ie but now Firefox is failing.
I was hoping to find a solution without writing an if statement to check for the functionality of .textContent or .innerText.
Is there a cross browser solution .getTheText?
If not what would be the best way around this? Is there a simple solution? (I ask given my knowledge and experience with scripting, which is limited)
** added following comments **
If this is my code block:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
prd.brand = brand[i].innerText;
prd.name = name[i].innerText;
prd.price = price[i].innerText;
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
Then if I understand the syntax from the comments and the question linked to in the comment, is this what I should do:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
prd.brand = brand[i].textContent || brand[i].innerText;
prd.name = name[i].textContent || name[i].innerText;
prd.price = price[i].textContent || price[i].innerText;
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
So using or with a double bar || assigns the first non null value?
Re: your edit, not quite. The way to access methods or properties on an object (eg a DOM element) is to use dot notation if you have the name itself, or square brackets in case of variables/expressions (also works with strings, as in obj["propName"], which is equivalent to obj.propName). You can also just test the property against one element and use that from there on:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
var txtProp = ("innerText" in brand[i]) ? "innerText" : "textContent"; //added string quotes as per comments
prd.brand = brand[i][txtProp];
prd.name = name[i][txtProp];
prd.price = price[i][txtProp];
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
Regarding the line:
var txtProp = (innerText in brand[i]) ? innerText : textContent;
The in keyword checks an object to access the property (syntax: var property in object). As for the question notation (I made an error earlier, using ||, the correct thing to use was a :),
var myVar = (prop in object) ? object[prop] : false;
As an expression, it basically evaluates the stuff before the ?, and if it's true, returns the expression before the :, else the one after. So the above is the same as / a shorthand for:
if(prop in object){
var myVar = object[prop];
}
else{
var myVar = false;
}
Since you are checking between two properties only and wanting to assign one or the other, the shortest way would indeed be:
var txtProp = brand[i].innerText || brand[i].textContent;
It would basically test the first property, and if it were false or undefined, it would use the second one. The only reason I (pedantically) avoid using this is because the first test of a || b would fail even if a existed but just had a value of 0, or an empty string (""), or was set to null.

Get control attributes with jQuery and create json

I have multiple checkboxes in a view and each one has some data attributes, example:
Once the button is clicked I'm iterating through all the checkboxes which are selected and what I want to do is get the data-price and value fields for each selected checkbox and create JSON array.
This is what I have so far:
var boxes2 = $("#modifiersDiv :checkbox:checked");
var selectedModifiers = [];
var modifierProperties = [];
for (var i = 0; i < boxes2.length; i++) {
for (var k = 0; k < boxes2[i].attributes.length; k++) {
var attrib = boxes2[i].attributes[k];
if (attrib.specified == true) {
if (attrib.name == 'value') {
modifierProperties[i] = attrib.value;
selectedModifiers[k] = modifierProperties[i];
}
if (attrib.name == 'data-price') {
modifierProperties[i] = attrib.value;
selectedModifiers[k] = modifierProperties[i];
}
}
}
}
var jsonValueCol = JSON.stringify(selectedModifiers);
I'm not able to get the values for each checkbox and I'm able to get the values only for the first one and plus not in correct format, this is what I'm getting as JSON:
[null,"67739",null,"1"]
How can I get the correct data?
You can use $.each to parse a jquery array, something like:
var jsonValueObj = [];
$("#modifiersDiv :checkbox:checked").each(function(){
jsonValueObj.push({'value':$(this).val(),'data-price':$(this).attr('data-price')});
});
jsonValueCol = JSON.stringify(jsonValueObj);
Please note it's generally better to use val() than attr('value'). More information on this in threads like: What's the difference between jQuery .val() and .attr('value')?
As for your code, you only had one answer at most because you were overwriting the result every time you entered your loop(s). Otherwise it was okay (except the formatting but we're not sure what format you exactly want). Could please you provide an example of the result you would like to have?
if you want to get an object with all checked values, skip the JSON (which is just an array of objects) and make your own....
var checked =[];
var getValues = function(){
$('.modifiers').each(function(post){
if($(this).prop('checked')){
checked.push({'data-price':$(this).attr('data-price'),'value':$(this).attr('value')});
}
});
}
getValues();
sure i'm missing something obvious here.. but mind is elsewhere
This should give an array with values (integers) and prices (floats):
var selected = [];
$("#modifiersDiv :checkbox:checked").each(function()
{
var val = parseInt($(this).val(), 10);
var price = parseFloat($(this).data("price"));
selected.push(val);
selected.push(price);
});
Edit: Updated answer after Laziale's comment. The $(this) was indeed not targeting the checked checkbox. Now it should target the checkbox.

Google Script to see if text contains a value

I have a google form that when the user submits it will trigger my function to run which is creating a summary of what they submitted as a Google Doc. I know it can automatically send an email but I need it formatted in a way that my user can edit it later.
There are some check boxes on the form -- but the getResponse() is only populated with the items checked and I need it to show all possible choices. Then I will indicate somehow what was checked.
I can't find a way to see if a text contains a value.
Like in Java with a String, I could do either .contains("9th") or .indexOf("9th") >=0 and then I would know that the String contains 9th. How can I do this with google scripts? Looked all through documentation and I feel like it must be the easiest thing ever.
var grade = itemResponse.getResponse();
Need to see if grade contains 9th.
Google Apps Script is javascript, you can use all the string methods...
var grade = itemResponse.getResponse();
if(grade.indexOf("9th")>-1){do something }
You can find doc on many sites, this one for example.
Update 2020:
You can now use Modern ECMAScript syntax thanks to V8 Runtime.
You can use includes():
var grade = itemResponse.getResponse();
if(grade.includes("9th")){do something}
I had to add a .toString to the item in the values array. Without it, it would only match if the entire cell body matched the searchTerm.
function foo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('spreadsheet-name');
var r = s.getRange('A:A');
var v = r.getValues();
var searchTerm = 'needle';
for(var i=v.length-1;i>=0;i--) {
if(v[0,i].toString().indexOf(searchTerm) > -1) {
// do something
}
}
};
I used the Google Apps Script method indexOf() and its results were wrong. So I wrote the small function Myindexof(), instead of indexOf:
function Myindexof(s,text)
{
var lengths = s.length;
var lengtht = text.length;
for (var i = 0;i < lengths - lengtht + 1;i++)
{
if (s.substring(i,lengtht + i) == text)
return i;
}
return -1;
}
var s = 'Hello!';
var text = 'llo';
if (Myindexof(s,text) > -1)
Logger.log('yes');
else
Logger.log('no');

Javascript - For each Name set ID equal to Name[i] AND validate

Okay I've created an invoice where depending on the number of invoiced items, it will generate a check box next to each one for approval. Each checkbox will have the name invoiceApproval.
So far my script will count to see how many inputs have the name invoiceApproval then assign an ID to each one with the ID = invoiceApproval[i]
That is working perfectly, but now when I use the JavaScript Form Validation : quick and easy! in HTML Forms to assign a frmvalidator.addValidation for each ID (the number of each being equal to how many checkboxes have the name invoiceApproval) it only validates the first box with the ID invoiceApproval0.
Why will my 'for each' only work for the first instance??? What am I doing wrong here:
<script type="text/javascript">
var frmvalidator = new Validator("forminvoice");
var inva = document.getElementsByName("invoiceApproval");
for (var i = 0; i < inva.length;i++) {
var inc = "invoiceApproval"+[i];
inva[i].setAttribute("id",inc);
}
var inva = document.getElementsByName("invoiceApproval");
for (var i = 0; i < inva.length;i++) {
var inc = "invoiceApproval"+[i];
frmvalidator.addValidation(inc,"shouldselchk=x","You must check off each item on the right hand side of the invoice before you can submit approval.");
}
</script>
And the PHP loop that generates the checkboxes is:
while($row = mysql_fetch_array($result))
{
echo "<input type='checkbox' value='x' name ='invoiceApproval' > Approve";
}
The library's code is using
var itemobj = this.formobj[itemname];
to retrieve the form item. Later it does
if (itemobj.length && isNaN(itemobj.selectedIndex))
{
itemobj = itemobj[0];
}
Which means that if there are multiple items with the same name, it will always choose the first one. Taking names for references to your form fields is such bad practice
The only solution (if you want to work with this library, but I would stay away from it, it's really poorly written) is to make your names not be the same.
Side Note
The line
var inc = "invoiceApproval"+[i];
Is creating an array with a single value in it, and then toString is being called on it, and toString (luckily for you) returns a single value array as the value itself. Get rid of those brackets, it should be
var inc = "invoiceApproval"+i;

Categories