I wanna use a variable as id name in selectQuery but I fail.
Hotelno is the variable of id name. How could I fix it so I could use that variable as id name ?
var hotel_1 = {
'name':"Hullett House",'url':"http://www.hulletthouse.com/",'flag':true,'photo':"../image/0.png",
'des':"Hullett House is perfectly located for both business and leisure guests in Hong Kong.",
'price':"HK$1800" };
var hotel_2 = {
'name':"Heritage Lodge",'url':"https://www.royalplaza.com.hk",'flag':false,'photo':"../image/14831652.jpg",
'des':"Heritage Lodge is located in the Western District. ",
'price':"HK$1120" };
var hotel_3 = {
'name':"Royal Plaza",'url':"https://www.royalplaza.com.hk",'flag':false,'photo':"../image/44228962.jpg",
'des':"Royal Plaza Hong Kong is located very close to shopping malls, trains and markets. ",
'price':"HK$2000" };
var hotel = [hotel_1,hotel_2,hotel_3];
function getHotel() {
var noHotel=Math.floor(Math.random()*3);
for (var i = 0; i<noHotel; i++) {
var hotelno = 'hotel'+ (i+1);
document.getElementById(hotelno).style.visibility="visible";
document.querySelector('#hotel1 .title').innerHTML=hotel[i].name; //can work
document.querySelector('#hotel1 img').src= hotel[i].photo; //can work
document.querySelector('"#"+hotelno .line').innerHTML=hotel[i].des; //can not work
}}
Related
WARNING: I'm not a programmer by trade.
Ok. Got the disclaimer out of the way. So this might not be the best way to do this but here is the scenario. I have a dropdown that gets populated via a Google Sheet. The user chooses a selection from the list but this dropdown does not have all of the possible values it could have. There will likely be a time when the user needs a new value added. While I could manually update the spreadsheet as new values are requested that introduces an element of human availability to get this done and I'm not always available.
What I would prefer is a self-serve model. I want to supply the user with a text field where they can enter the new value and submit it to the Google Sheet. Then I would like the dropdown to be updated with the new value for the user to choose.
Now, I realize that I could just submit the value in the new field to the Google Sheet but that will require building a condition to see whether it is the dropdown or text field that has a value in it. I'd also need some type of error handling in case both the dropdown and text field have values. That seems like a bigger headache to program then my ask.
I'm not sure what code you would need to see to help make this work but here is what I think might help.
doGet function
function doGet(e){
var ss = SpreadsheetApp.openById(ssId)
var ws = ss.getSheetByName("External");
var range = ws.getRange("A2:D2");
var valuesArray = [];
for (var i = 1; i <= range.getLastColumn(); i++){
var lastRowInColumn = range.getCell(1, i).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();
var list = ws.getRange(2,i,lastRowInColumn-1,1).getValues();
valuesArray.push(list);
}
var userEmail = Session.getActiveUser().getEmail();
var sourceListArray = valuesArray[2].map(function(r){ return '<option>' + r[0] + '</option>'; }).join('');
var productListArray = valuesArray[3].map(function(r){ return '<option>' + r[0] + '</option>'; }).join('');
var tmp = HtmlService.createTemplateFromFile("config");
tmp.productList = productListArray;
return tmp.evaluate();
}
Add to Google Sheet
function userClicked(tagInfo){
var ss = SpreadsheetApp.openById(ssId)
var ws = ss.getSheetByName("Data");
ws.appendRow([tagInfo.email, tagInfo.source, tagInfo.product, new Date()]);
}
Add record
function addRecord(){
var tagInfo = {};
tagInfo.product = document.getElementById("product").value;
google.script.run.userClicked(tagInfo);
var myApp = document.getElementById("source");
myApp.selectedIndex = 0;
M.FormSelect.init(myApp);
var myApp = document.getElementById("brand");
myApp.selectedIndex = 0;
M.FormSelect.init(myApp);
var myApp = document.getElementById("product");
myApp.selectedIndex = 0;
M.FormSelect.init(myApp);
}
How dropdowns are populated in the HTML.
<div class="input-field col s3">
<select id="product" onchange="buildURL()">
<option disabled selected value="">Choose a product</option>
<?!= productList; ?>
</select>
<label>Product</label>
</div>
Need to see anything else? I think it might be relatively easy to add the new value to the column but the tricky part seems to be the update of only that one dropdown and not the entire app. To me it seems like I want to trigger the doGet() function again but only for that specific dropdown. Thoughts?
UPDATE: current code to add new value to dropdown
function addProduct() {
let newProd = document.getElementById("newProduct").value;
google.script.run.withSuccessHandler(updateProductDropdown).addNewProduct(newProd);
document.getElementById("newProduct").value = "";
}
function updateProductDropdown(newProd){
var newOption = document.createElement('option');
newOption.value = newProd;
newOption.text = newProd;
document.getElementById('product').add(newOption);
}
UPDATE2: App Scripts function to add new value to column in spreadsheet
function addNewProduct(newProd){
var columnLetterToGet, columnNumberToGet, direction, lastRow, lastRowInThisColWithData, rng, rowToSet, startOfSearch, valuesToSet;
var ss = SpreadsheetApp.openById(ssId);
var ws = ss.getSheetByName("List Source - External");
lastRow = ws.getLastRow();
//Logger.log('lastRow: ' + lastRow)
columnNumberToGet = 9;//Edit this and enter the column number
columnLetterToGet = "I";//Edit this and enter the column letter to get
startOfSearch = columnLetterToGet + (lastRow).toString();//Edit and replace with column letter to get
//Logger.log('startOfSearch: ' + startOfSearch)
rng = ws.getRange(startOfSearch);
direction = rng.getNextDataCell(SpreadsheetApp.Direction.UP);//This starts
//the search at the bottom of the sheet and goes up until it finds the
//first cell with a value in it
//Logger.log('Last Cell: ' + direction.getA1Notation())
lastRowInThisColWithData = direction.getRow();
//Logger.log('lastRowInThisColWithData: ' + lastRowInThisColWithData)
rowToSet = lastRowInThisColWithData + 1;
valuesToSet = [newProd];
ws.getRange(rowToSet, 9).setValues([valuesToSet]);
return newProd;
}
SOLUTION to Update Materialize Dropdown
function updateProductDropdown(newProd){
newProdOption = document.getElementById('product');
newProdOption.innerHTML += '<option>' + newProd + '</option>';
var elems = document.querySelectorAll('select');
var instances = M.FormSelect.init(elems);
}
You can specify a client side callback function if you use google.script.run withSuccessHandler(callback) where your callback could update the list only and not the whole site.
Example:
google.script.run.withSuccessHandler(updateDropdownWidget).updateDropdownList(text_from_input)
Where updateDrownList(text_from_input) is a function in your Apps Script that adds text to the sheet using SpreadsheetApp for example, and returns the "text" to the callback function: updateDropdownWidget(text) which adds a new list item to the HTML drop-down list in your front end.
index.html:
<form>
<label for="newOption">New option for the dropdown:</label>
<input type="text" id="nopt" name="newOption">
<input type="button" value="Submit"
onclick="google.script.run.withSuccessHandler(updateDropdownWidget)
.updateDropdownList(document.getElementById('nopt').value)">
</form>
<label for="cars">Choose a car:</label>
<select name="cars" id="cars">
<?!= values; ?>
</select>
<script>
function updateDropdownWidget(text){
var option = document.createElement('option');
option.value = text;
option.text = text;
document.getElementById('cars').add(option);
}
</script>
Code.gs:
function doGet(e){
var ss = SpreadsheetApp.getActiveSheet();
var lastRow = ss.getDataRange().getLastRow();
var values = ss.getRange(1,1,lastRow,1).getValues();
var valuesArray = [];
for (var i = 0; i < values.length; i++){
valuesArray.push('<option value="'+values[i]+'">' +values[i]+ '</option>');
}
var tmp = HtmlService.createTemplateFromFile("index");
tmp.values = valuesArray;
return tmp.evaluate();
}
function updateDropdownList(text_from_input){
// Log the user input to the console
console.log(text_from_input);
// Write it to the sheet below the rest of the options
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getDataRange().getLastRow();
sheet.getRange(lastRow+1,1).setValue(text_from_input);
// Return the value to the callback
return text_from_input;
}
Here's an example:
In my Stack Over Flow spreadsheet I four buttons which can be used to run any function in 3 script files and every time I load the sidebar it reads the functions in those script files and returns them to each of the select boxes next to each button so that I test functions that I write for SO with a single click and I can select any function for any button. Here's the Javascript:
$(function(){//JQuery readystate function
google.script.run
.withSuccessHandler(function(vA){
let idA=["func1","func2","func3","func4"];
idA.forEach(function(id){
updateSelect(vA,id);
});
})
.getProjectFunctionNames();
})
Here is GS:
function getProjectFunctionNames() {
const vfilesA=["ag1","ag2","ag3"];
const scriptId="script id";
const url = "https://script.googleapis.com/v1/projects/" + scriptId + "/content?fields=files(functionSet%2Cname)";
const options = {"method":"get","headers": {"Authorization": "Bearer " + ScriptApp.getOAuthToken()}};
const res = UrlFetchApp.fetch(url, options);
let html=res.getContentText();
//SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Project Functions");
let data=JSON.parse(res.getContentText());
let funcList=[];
let files=data.files;
files.forEach(function(Obj){
if(vfilesA.indexOf(Obj.name)!=-1) {
if(Obj.functionSet.values) {
Obj.functionSet.values.forEach(function(fObj){
funcList.push(fObj.name);
});
}
}
});
//SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(funcList.join(', ')), "Project Functions");
return funcList;//returns to withSuccessHandler
}
Image:
Animation:
I'm struggling to understand how I change which variable is being added to in a function.
Essentially, when a user chooses a hotel, it needs to be added to hotelSelection. When they click next, it updates a map with restaurants, and when they choose restaurants, it needs to go into restaurantSelection. That's the bit I'm stuck on, changing it from hotel selection to restaurant selection.
Here's a snippet:
var hotelSelection = "";
var restaurantSelections = "";
var sightSelections = "";
function chooseSelection(resultIndex) {
var locationName = document.getElementById('locationName-' + resultIndex);
hotelSelection = `<div class="input-group" id="Hotel-chosen">
<li class="list-group-item">
<strong>${locationName.innerHTML}</strong><br>`;
var locationRating = document.getElementById('locationRating-' +
resultIndex);
hotelSelection += `rating: ${locationRating.innerHTML}</li>`
console.log("Hotel Selection: " + hotelSelection);
}
Any help would be really appreciated!
This gets to the heart of how a function can communicate. What you are doing is accessing a variable in a higher "closure." This has its good and bad points, and one of the bad points which you're finding now is that it isn't very flexible.
If instead of doing it this way you were able to use the return value of the function, you would have the flexibility to do what you're saying. For example:
var hotelSelection = "";
var restaurantSelections = "";
var sightSelections = "";
function chooseSelection(resultIndex) {
var locationName = document.getElementById('locationName-' + resultIndex);
var selection = `<div class="input-group" id="Hotel-chosen">
<li class="list-group-item">
<strong>${locationName.innerHTML}</strong><br>`;
var locationRating = document.getElementById('locationRating-' +
resultIndex);
selection += `rating: ${locationRating.innerHTML}</li>`
console.log("Hotel Selection: " + hotelSelection);
return selection;
}
If you do this, then when you call the function you just assign the return value to a variable, like this:
hotelSelection = chooseSelection(someIndex);
sightSelections = chooseSelection(someIndex);
I'm trying to do a script which pauses an adgroup based in the field AVAILABILITY in a XML (a Google Merchant XML).
I have a merchant XML, and want to use the ID (the id is algo used in the adgroup name) and AVAILABILITY (In Stock, Out of Stock, Preorder) from the XML to pause Adgroups in my campaign.
I tried to modify an example, I had found here, using adgroups in place of campaigns, but I think the error is when the script try to read the XML.
In the lines bellow lines I receive the error: "TypeError: Cannot call method "getValue" of null. (line 16)"
var id = entries[i].getChild('g:id').getValue();
var availability = entries[i].getChild('g:availability').getValue();
My XML is like this:
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
<channel>
<item>
<title>
<![CDATA[ Far Cry 4: Overrun ]]>
</title>
<link>https://www.nuuvem.com/item/far-cry-4-overrun</link>
<description>
<![CDATA[
Neste novo conteúdo para Far Cry 4, enquanto a guerra sem trégua por Kyrat continua, os jogadores assumirão o papel dos Rakshasa e do Caminho Dourado para manter locais que dão pontos no mapa da batalha.
]]>
</description>
<g:availability>in stock</g:availability>
<g:price currency="BRL">8.99</g:price>
<g:image_link> http://dskhvldhwok3h.cloudfront.net/image/upload/t_boxshot_big/v1/products/557dbcb269702d0a9c490801/boxshots/ynhjodwlnmur0crkiaxp.jpg </g:image_link>
<g:product_type>
<![CDATA[ Ação ]]>
</g:product_type>
<g:google_product_category>Software > Video Game Software > Computer Games</g:google_product_category>
<g:condition>new</g:condition>
<g:identifier_exists>FALSE</g:identifier_exists>
<g:id>2668</g:id>
</item>
My script now looks like this:
function main() {
// Load an XML file:
var xmlURL = "XML URL HERE";
var xmlFile = UrlFetchApp.fetch(xmlURL);
// Parse the XML file:
var document = XmlService.parse(xmlFile);
var root = document.getRootElement();
// Go through all children of <deepdive_pages>:
var entries = document.getRootElement().getChildren();
for (var i = 0; i < entries.length; i++) {
//for (var i = 0; i < 10; i++) {
var id = entries[i].getChild('g:id').getValue();
var availability = entries[i].getChild('g:availability').getValue();
// If company_root_id has 0 jobs
if (availability == "out of stock") {
var adGroupIterator = AdWordsApp.adGroups().withCondition('Name CONTAINS "| ' + id + ' |"').get(); // Find all campaigns with the company name
while (adGroupIterator.hasNext()) { // Go over all campaings with company id in the name
var adgroup = adGroupIterator.next();
if (adgroup.isEnabled()) { // If campaign is enables, pause it
adgroup.pause();
Logger.log("adgroup " + adgroup.getName() + " was paused.");
}
}
}
// If company_root_id has MORE than 0 jobs
else {
var adGroupIterator = AdWordsApp.adGroups().withCondition('Name CONTAINS "| ' + id + ' |"').get(); // Find all campaigns with the company name
while (adGroupIterator.hasNext()) { // Go over all campaings with company id in the name
var adgroup = adGroupIterator.next();
if (adgroup.isPaused()) { // If campaign is paused, enable it
adgroup.enable();
Logger.log("adgroup " + adgroup.getName() + " was enabled.");
}
}
} // End If Else
}
}
Many thanks for any help!
With the help of Calin in this post: https://www.en.advertisercommunity.com/t5/Advanced-Features/Script-to-pause-Adggroups-based-in-information-from-a-Merchant/m-p/1024590#
I solved my problem.
To read the XML correctly, I used:
// Load an XML file:
var xmlURL = "XML_MERCHANT_URL_HERE";
var xmlFile = UrlFetchApp.fetch(xmlURL);
// Parse the XML file:
var document = XmlService.parse(xmlFile);
var ns = XmlService.getNamespace("http://base.google.com/ns/1.0");
var rss = document.getRootElement().getChildren(); //root element is <rss>; it's children are actually only one, <channel>
var entries = rss[0].getChildren('item'); //getting all 'item' children of the first rss element, <channel>
// Go through all children of <deepdive_pages>:
Logger.log(entries.length);
for (var i = 0; i < entries.length; i++) {
var id = entries[i].getChild('id',ns).getText();
var availability = entries[i].getChild('availability',ns).getText();
//Logger.log("ID: "+id+", Availability: "+availability);
Before I did'nt declared the XML namespace and I was using getchild, but in this case I need to use getChildren.
I have been trying to lookup username using activeuser. Only the first part works and the last part doesnt. My goal is to unhide the sheet based on the username of the active user (sheetname is based on username). Below is the code I am using.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [];
menuEntries.push({name: "Test getActiveUser/getEmail", functionName: "onTest"});
ss.addMenu("Rep Drowndown", menuEntries);
testGetEmail("onOpen");
}
function onTest() {
testGetEmail("menu function");
};
function testGetEmail(callerId) {
var userEmail = "";
var activeUser = Session.getActiveUser();
if (activeUser == null)
Browser.msgBox("Session.getActiveUser() returned null", "called by " + callerId, Browser.Buttons.OK);
else
userEmail = activeUser.getEmail();
if (userEmail == "")
Browser.msgBox("Your Email returned an empty string", "called by " + callerId, Browser.Buttons.OK);
else
var ss = SpreadsheetApp.getActiveSpreadsheet();
var lookup = Session.getActiveUser().getEmail();
var range = ss.getRange('$A$3:$B$8').getValues();
var lookupRange = [];
for (var i = 0; i < range.length; i++)
lookupRange.push(range[i][0]);
var index = lookupRange.indexOf(lookup);
if (index == -1) {
// implicit no-op
}
else {
var link = range[index][2]
var sheet = ss.getSheetByName(link);
sheet.showSheet();
};
}
You need to set up an installable trigger. This allows the script to run with authorization, whereas normally "onOpen()" is run as a Simple Trigger. Since Simple Triggers can't authorize as users, you'll never get an email address.
The simplest solution is to set testGetEmail() to be run on open. Do this within the Script Editor by choosing Resources > Current Project's Triggers in the menu. Then click "No triggers set up. Click here to add one now." Finally, set up your trigger:
Choose your function's name (testGetEmail) from the first dropdown under "Run."
Choose "From spreadsheet" in the second dropdown under "Events."
Choose "On Open" in the third dropdown.
Then test to be sure I didn't commit a typo :-)
I want to get the values that were entered by the user from the window prompts, and let them display in paragraph tags on by html page. I thought using document.getElementById("idName").innerHTML would work, but it doesn't show the values that were entered by the user.
Here is the code I have on my JavaScript page. I have called the methods that need to execute on the page. :
var players = [];
var numberOfMoves = 0;
var currentPlayer = 0;
getPlayerNames(); //Get player names
setPlayerInformation(); //Set the names of the players in paragraph tags with id playerOneInformation and playerTwoInformation
function getPlayerNames()
{
players[0] = window.prompt("Please Enter Player 1: ", "");
players[1] = window.prompt("Please Enter Player 2: ", "");
}
function setPlayerInformation()
{
document.getElementById("playerOneInformation").innerHTML = players[0];
document.getElementById("playerTwoInformation").innerHTML = players[1];
}