I am working on a GAS powered dependent dropdown, which I have now come very close to achieving with the help from stackoverflow, thanks to all.
But I have come to a point where there is a lack of documentation? Or answer to.
I am trying to get a specific column by name, by that I find the column that has the header/top row value of a certain input. In this case it's a dropdown running on an HTML page, that speaks with GAS.
Most of the script works just fine, it's the building what I call topics in the buttom of the HTML and the Code.gs that is causing me some trouble. Right now it's not getting the columns in anyway, but that's because I haven't found a way of doing it yet. The parts that are not working right now are: function buildTopicsList(rowsName) in the HTML and the function getTopics(subCategories,categories) in Code.gs
Here I want to get all the rows in a column with a "header" selected from a dropdown in the html.
The full HTML looks like this:
index.html
<div>
<select id="categories" onchange="google.script.run.withSuccessHandler(buildSubCategoriesList)
.getSubCategories(this.value)">
<option>Loading...</option>
</select>
<select id="subCategories" onchange="google.script.run.withSuccessHandler(buildTopicsList)
.getTopics(this.value)">
<option>Loading...</option>
</select>
<select id="topics">
<option>Loading...</option>
</select>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
// This code in this function runs when the page is loaded.
$(function() {
google.script.run.withSuccessHandler(buildCategoriesList).getCategories();
});
function buildCategoriesList(sheetsName) {
var list = $('#categories');
list.empty();
for (var i = 0; i < sheetsName.length; i++) {
list.append('<option value="' + sheetsName[i] + '">' + sheetsName[i] + '</option>');
}
list.trigger("change");
}
function buildSubCategoriesList(columnsName) {
console.log(columnsName);
var list = $('#subCategories');
list.empty();
for (var i = 0; i < columnsName.length; i++) {
list.append('<option value="' + columnsName[i] + '">' + columnsName[i] + '</option>');
}
list.trigger("change");
}
function buildTopicsList(rowsName) {
console.log(rowsName);
var list = $('#topics');
list.empty();
for (var i = 0; i < rowsName.length; i++) {
list.append('<option value="' + rowsName[i] + '">' + rowsName[i] + '</option>');
}
}
</script>
and the code:
Code.gs
var ss = SpreadsheetApp.openById("1BK5urtTzqZ2kc89ZnbeMSIm2-bt4KLNTQghsxn0cXBI");
function doGet(request) {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getCategories(){
var sheetsName = [];
var sheets = ss.getSheets();
for( var i = 0; i < sheets.length; i++ ){
sheetsName.push( sheets[i].getName() )
}
return sheetsName;
}
function getSubCategories(categories){
var columnsName = [];
var sheet = ss.getSheetByName(categories);
var subRange = sheet.getRange(1, 1, 1, sheet.getLastColumn());
var columns = subRange.getValues()[0];
for( var i = 0; i < columns.length; i++ ){
columnsName.push( columns[i] )
}
return columnsName;
}
// this here is not working:
function getTopics(subCategories,categories){
var rowsName = [];
var sheet = ss.getSheetByName(categories);
var topRange = sheet.getRange(1, 1, 1, sheet.getLastColumn());
var rows = topRange.getValues()[0];
for( var i = 0; i < rows.length; i++ ){
rowsName.push( rows[i] )
}
return rowsName;
}
Any suggestions?
The server function getTopics(subCategories,categories) is being called by onchange attribute:
onchange="google.script.run.withSuccessHandler(buildTopicsList)
.getTopics(this.value)"
this.value is being passed to the getTopics(subCategories,categories) function. But the getTopics(subCategories,categories) function accepts two parameters, not one. And the first parameter, subCategories is never being used in the code. That's why you aren't getting anything. Put in a Logger.log('categories: ' + categories); statement at the top of the getTopics(subCategories,categories) function, and I'll bet that it shows nothing.
As a "side note". The doGet() function is using the createTemplateFromFile() method, but there are no scriptlets in your index.html file. The index.html file is not a template. If you were using a template, you wouldn't need to run some of the browser code when the html loads. I'm not saying that it's better to use the scriptlets, or suggesting you do that. But I just wanted you to know what is happening and what your options are.
Related
I have a for loop which looks like this:
for (var i = 0; i < list.length; i++) {
It is looping through Firebase data in the database and returning all the data in the database.
However, I want it to only go up to the first 10 database items. So I changed the loop to:
for (var i = 0; i < 9; i++) {
But this fails to display any results when the there are less than 10 pieces of data in the database. However, if I set the number to however many objects I have in the database, for example 10 because I have 10 objects, it displays them all. But any less than this number and I just get a blank webpage.
Here is the webpage when I have 10 objects in my Firebase database:
And here it is when I remove one of those objects:
I have no idea why this is happening - The logic is correct - if i is less than 9 then display the data - But instead it only displays it when it equals 9.
Here is the full JS:
function refreshUI(list) {
var lis = '';
var lis2 = '';
var lis3 = '';
var lis4 = '';
for (var i = 0; i <= 9; i++) {
lis += '<li data-key="' + list[i].key + '" onclick="addText(event)">' + list[i].book + '</li>';
lis2 += genLinks(list[i].key, list[i].book)
};
for (var i = 10; i < list.length; i++) {
lis3 += '<li data-key="' + list[i].key + '" onclick="addText(event)">' + list[i].book + '</li>';
lis4 += genLinks(list[i].key, list[i].book)
};
document.getElementById('bookList').innerHTML = lis;
document.getElementById('bookList2').innerHTML = lis2;
document.getElementById('bookList3').innerHTML = lis3;
document.getElementById('bookList4').innerHTML = lis4;
};
function genLinks(key, bkName) {
var links = '';
links += '<img src="images/bin.png" style="width: 24px; height: 24px; transform: translateY(-7px); opacity: .4;"></img> ';
return links;
};
function del(key, bkName) {
var response = confirm("Are certain about removing \"" + bkName + "\" from the list?");
if (response == true) {
// build the FB endpoint to the item in movies collection
var deleteBookRef = buildEndPoint(key);
deleteBookRef.remove();
}
}
function buildEndPoint (key) {
return new Firebase('https://project04-167712.firebaseio.com/books/' + key);
}
// this will get fired on inital load as well as when ever there is a change in the data
bookList.on("value", function(snapshot) {
var data = snapshot.val();
var list = [];
for (var key in data) {
if (data.hasOwnProperty(key)) {
book = data[key].book ? data[key].book : '';
if (book.trim().length > 0) {
list.push({
book: book,
key: key
})
}
}
}
// refresh the UI
refreshUI(list);
});
If anybody has any help I'd greatly appreciate it!
When the list size is shorter than 10, you will get an error in the loop because you will eventually address a property (like key) that does not exist on list[i] (since it is undefined). If you would check the console, you would notice that this error is reported.
To fix this, change the condition of the first for loop like this:
for (var i = 0; i < Math.min(10, list.length); i++) {
This way, the loop will never iterate to an entry that does not exist. It will stop after 9 or after list.length-1 whichever comes first.
Alternatively, you can just put the two conditions with an && operator:
for (var i = 0; i < 10 && i < list.length; i++) {
Update: I've tried the suggestions in the comments and it's still not working. I really have no idea why. I've consolidated it to a single loop and fixed the syntax errors noted. Here's the code as it looks now:
$(function() {
$("#json-one").change(function() {
var $dropdown = $(this);
$.getJSON("washroutines.json", function(data) {
var vals = [];
var $jsontwo = $("#json-two");
$jsontwo.empty();
for (var i = 0; i < data.length; i++){
if (data[i].make === $dropdown.val()) {
$jsontwo.append("<option value=\"" + data[i].model + "\">" + data[i].model + "</option>");
}
}
});
});
});
Any additional help would be much appreciated!
Original question:
I'm trying to create dependent drop down menus using a json object, and I'm having trouble getting the second menu to populate based on the first. When the first menu changes, the second goes to a bunch of "undefined"s.
$.getJSON("washroutines.json", function(data) {
var vals = [];
for (i = 0; i < data.length; i++){
if (data.make = $dropdown.val()) {
vals.push(data.model);
}
}
var $jsontwo = $("#json-two");
$jsontwo.empty();
for (i = 0; i < vals.length; i++){
$jsontwo.append("<option value\"" + vals[i] + "\">" + vals[i] + "</option>");
}
Please use small words when explaining things to me, I'm new at this!
contents of the JSON:
[{"make":"Maytag","model":"Bravos","prewashCycle":"Whitest Whites"},
{"make":"Maytag","model":"Awesome","prewashCycle":"Awesome Whitest Whites"},
{"make":"Whirlpool","model":"Cabrio","prewashCycle":"Extra Heavy"},
{"make":"Kenmore","model":"Elite","prewashCycle":"Awesome"}]
Try changing your for loop for this
for (var i = 0; i < data.length; i++){
if (data[i].make === $dropdown.val()) {
vals.push(data[i].model);
}
}
I am working on a dynamic dependent dropdown, and I have gotten very far and learned alot through stackoverflow, so thanks to everyone. Anyways Iv'e reached a stopping point where something isn't working?
Here is a url for the online script: https://script.google.com/macros/s/AKfycbzWi3JymieWMAGVLQVl2xEXCd_eo85hWercHAFkNrqH5dkvWWQd/exec
and a url to the spreadsheet: https://docs.google.com/spreadsheets/d/1BK5urtTzqZ2kc89ZnbeMSIm2-bt4KLNTQghsxn0cXBI/edit#gid=0
Two lists are generated the first one lists all the sheets in the spreadsheet, and the second one, should, list the values in the first row.
So below you see my index.html:
index.html
<div>
<select id="categories" onchange="google.script.run.withSuccessHandler(buildSubCategoriesList)
.getSubCategories()">
<option>Loading...</option>
</select>
<select id="subCategories">
<option>Loading...</option>
</select>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<script>
// This code in this function runs when the page is loaded.
$(function() {
google.script.run.withSuccessHandler(buildCategoriesList)
.getCategories();
});
function buildCategoriesList(sheetsName) {
var list = $('#categories');
list.empty();
for (var i = 0; i < sheetsName.length; i++) {
list.append('<option value="' + sheetsName[i] + '">' + sheetsName[i] + '</option>');
}
}
</script>
<script>
function buildSubCategoriesList(columnsName) {
var list = $('#subCategories');
list.empty();
for (var i = 0; i < columnsName.length; i++) {
list.append('<option value="' + columnsName[i] + '">' + columnsName[i] + '</option>');
}
}
</script>
and here is the code:
code.gs
function doGet(request) {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
var ss = SpreadsheetApp.openById("1BK5urtTzqZ2kc89ZnbeMSIm2-bt4KLNTQghsxn0cXBI");
function getCategories(){
sheetsName = [];
var sheets = ss.getSheets();
for( var i = 0; i < sheets.length; i++ ){
sheetsName .push(sheets[i].getName() )
};
return sheetsName;
}
function getSubCategories(categories){
columnsName = [];
var sheet = ss.getSheetByName(categories);
var lastCol = sheet.getLastColumn();
var subRange = sheet.getRange("A1"+lastCol);
var columns = subRange.getValues;
for( var i = 0; i < sheets.length; i++ ){
columnsName .push(columns[i])
};
return columnsName;
}
I have tried alot of things, any I really can't seem to figure out a way to make this work. So I am asking you guys, any suggestions?
A couple of things have to be corrected in your code for it to work:
index.html file: your call to getSubCategories() function in onchange event on categories select did not pass the selected value
index.html file: populating a <select> element with options does not trigger change event - thus you need to trigger it manually to update Sub-categories select
code.gs file: in getSubCategories() function, your getRange() call retrieved 3 rows in column A instead of 3 cell in row 1
code.gs file: again in getSubCategories() function your getValues call has to be getValues(); and since getValues() returns a 2D array, you have to get column values from the first array item only (first row), hence it should be getValues()[0]
Corrected working code attached below:
code.gs
var ss = SpreadsheetApp.openById("1BK5urtTzqZ2kc89ZnbeMSIm2-bt4KLNTQghsxn0cXBI");
function doGet(request) {
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getCategories(){
var sheetsName = [];
var sheets = ss.getSheets();
for( var i = 0; i < sheets.length; i++ ){
sheetsName.push( sheets[i].getName() )
}
return sheetsName;
}
function getSubCategories(categories){
var columnsName = [];
var sheet = ss.getSheetByName(categories);
var subRange = sheet.getRange(1, 1, 1, sheet.getLastColumn());
var columns = subRange.getValues()[0];
for( var i = 0; i < columns.length; i++ ){
columnsName.push( columns[i] )
}
return columnsName;
}
index.html
<div>
<select id="categories" onchange="google.script.run.withSuccessHandler(buildSubCategoriesList)
.getSubCategories(this.value)">
<option>Loading...</option>
</select>
<select id="subCategories">
<option>Loading...</option>
</select>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
// This code in this function runs when the page is loaded.
$(function() {
google.script.run.withSuccessHandler(buildCategoriesList).getCategories();
});
function buildCategoriesList(sheetsName) {
var list = $('#categories');
list.empty();
for (var i = 0; i < sheetsName.length; i++) {
list.append('<option value="' + sheetsName[i] + '">' + sheetsName[i] + '</option>');
}
list.trigger("change");
}
function buildSubCategoriesList(columnsName) {
console.log(columnsName);
var list = $('#subCategories');
list.empty();
for (var i = 0; i < columnsName.length; i++) {
list.append('<option value="' + columnsName[i] + '">' + columnsName[i] + '</option>');
}
}
</script>
I have a select menu that, on change through AJAX, generates another select menu, which in turn on change generates yet another select menu with yet another callback on change (lets say alert(chosenValue)). By working my way through these select menus the first time, I generate correctly the content for the second and third select, and the alert is printed correctly.
From the second time onward, when I generate new content for the third select, when choosing an option, multiple alerts are fired with the elements that were at that position in the select. I am guessing that these callbacks where generated and stayed there, because I generated them with $("select#dropdownMenu3").change(function() {...});.
Is there a way to remove such generated callbacks and have a "virgin" select#dropdownMenu3 again? I'm using jQuery, so big pluses if the solution includes it! Thanks!
EDIT: code becasue - reEDIT: I'm starting to thing that the problem is far more serious. Here is the whole thing
/*All the variables that you see used are defined somewhere up here*/
$("select#dropdownMenu1").change(function(){
$('select#dropdownMenu3').unbind('change');
var typeChosen = $(this).children(":selected").html();
if(!searchSuggestions){
$.get( "/searchdata", function( data ) {
data = JSON.parse(data);
searchSuggestions = data;
for(var i = 0; i < data.length; i++){
if(data[i].type == typeChosen){
$("#dropdownMenu2").empty();
$("#dropdownMenu2").append("<option disabled>Choose a Predicate</option>");
for(var j = 0; j < data[i].predicates.length; j++){
$("#dropdownMenu2").append("<option value='" + j + "'>" + data[i].predicates[j].predicate + "</option>");
}
}
}
$("#dropdownMenu2").select2();
$("select#dropdownMenu2").change(function(){
predicateChosenIndex = parseInt($(this).children(":selected").val());
$("#tdDropdownMenu3").html("");
$('#tdDropdownMenu3').append("<select class=\"js-example-basic-multiple\" id=\"dropdownMenu3\"><option disabled>Choose an Object</option></select>");
for(var i = 0; i < data.length; i++){
if(data[i].type == typeChosen){
for(var k = 0; k < data[i].predicates[predicateChosenIndex].values.length; k++){
$("#dropdownMenu3").append("<option value='" + k + "'>" + data[i].predicates[predicateChosenIndex].values[k].value + "</option>");
}
}
}
$("#dropdownMenu3").select2();
$("select#dropdownMenu3").change(function(){
objectChosenIndex = parseInt($(this).children(":selected").val());
for(var i = 0; i < data.length; i++){
if(data[i].type == typeChosen){
alert(data[i].predicates[predicateChosenIndex].values[objectChosenIndex].value + " chosen");
}
}
});
});
});
}
You can use:
$('select#dropdownMenu3').unbind('change');
But as long as we don't see how you generate your html, this might not be the best solution.
I have three javascript variables by name Area, Device and Problem. I want to store values of these variables into a CSV file one by one as given in the image attached with this and download that sheet on click of download button.Both jsp and javascript are in same file.i have tried the below so far.
Sample.jsp
<form>
<input type="BUTTON" id="DownloadBtn" value="Download">
</form>
<script type="text/javascript">
var Area=["Area1","Area2"];
var Device=[["device1","device2"],["device3","device4"]];
var Problem=[[["problem1","problem2"]],[["problem3","problem4"]],[["problem5","problem6"]],[["problem7","problem8"]]];
$("#DownloadBtn").click(function() {
var resp=confirm("Do You Want To Download?");
if(resp){
var last = '';
for (var i = 0; i < Area.length; i++) {
var first = Area[i];
for (var j = 0; j < first .length; j++) {
var second= device[j];
for(var k=0;k<second.length;k++){
var third=problem[k];
var value= third.replace(/"/g, '""');
if (value.search(/("|,|\n)/g) >= 0)
value= '"' + value+ '"';
if (k > 0)
last += ',';
last += value;
}
}
last += '\n';
}
var anchor= document.createElement('a');
anchor.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(last ));
anchor.setAttribute('download', 'sample.csv');
anchor.click();
}
});
</script>
Please suggest me any kind of changes or modification needed to get desired pattern. how this can be achieved as required in the image. Comment below for any kind of clarification on this question. Any suggestion would be welcome.