I have a Google sheet named 'Data' where a user can enter their own product names and sale numbers associated with it (meant to be a template so the product names may change user to user). I have read in the data and the product names (PRODUCT_NAMES) into some global data objects so I have access to it. I have created a menu item called "Plot Data" which goes to function (for now) testFunction()
I would like to display a form to the user that displays a checkbox grid item of all their product names (and maybe more future options), based on which ones they check I create a graph or calculate some values.
My question is two part:
1) How to create a form with dynamic grid choices, say one user has
PRODUCT_NAMES = {"Shirt", "Pants", "Shoes"}
and another has
PRODUCT_NAMES = {"Cars", "Trucks", "Suvs", "Trailers", "Boats"}
2) How to display the form in a google sheet? I have right now:
function testFunction() {
// Create a new form since the options change
var form = FormApp.create('Test Form');
var item = form.addCheckboxItem();
item.setTitle("This is a Test");
// TODO: Add dynamic options here
var formUrl = form.getPublishedUrl();
var response = UrlFetchApp.fetch(formUrl);
var formHtml = response.GetContextText();
var htmlApp = HtmlService
.createHtmlOutput(formHtml)
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('Sales Data')
.setWidth(500)
.setHeight(450);
SpreadsheetApp.getUi().showSidebar(htmlApp);
}
But this does not seem to work, I may be misunderstanding something
Question 2b) How to access the user responses.
I am learning javascript and GAS on the fly, so this is all new to me. I would like to try and keep the user options part as a Google Form, just cause its easier from an API perspective what is going on.
From my comment, expanded a bit, for future users:
It would be better to show a UI Modal Dialog with a basic HTML form. This keeps the code within the script itself.
The issue with what you want to do is once the form is made and the spreadsheet is linked the data will still go to a new sheet per form, not to where you want it to go, and then you'd have to setup a onEdit() trigger to get the values from the new sheet, generate charts, then delete the sheet and, somehow, the form. It is possible, but probably more work than a simple Modal Dialog.
Consider the HTML option as there are numerous simple-to-follow tutorials and examples and resources on the web. If you do end up trying it out, I'd suggest Bootstrap to start!
Good luck either way!
Related
I have a google doc that I'm using as a template, and it will be filled with the results of google forms data (which populates in a spreadsheet). The user will be checking off items on various lists on the form, and those items will get transferred over to the google doc template. On the doc template I want the script to be able to delete the header/initial bullet point space/blank space associated with any sections that don't end up having any items checked off, just to clean things up.
Is this possible? If so, how would I go about doing it or what kinds of terms should I be googling to figure it out?
Thank you for any help you can provide!
Apps Script, If this helps..
https://developers.google.com/apps-script/guides/docs
https://developers.google.com/apps-script/guides/sheets
As you might have figured it out, that Google Apps Script has vast variety of methods and options to work with, it is almost similar to javascript.
The issue related to this scenario might not be the first one, so, App Script already provided lots of options to cover up many things.
There are built-in functions which can be executed when your sheet loads, and you can schedule the script to run at some specific time of day and/or night by using Triggers in the Script Editor interface under Edit Menu.
It depends on your logic, when the script should be executed.
Sample deleting script is as follow:
function removeBlankRows(docId) {
var document = docId ?
DocumentApp.openById(docId) :
DocumentApp.getActiveDocument();
var body = document.getBody();
var search = null;
var tables = [];
// Extract all the tables inside the Google Document
while (search = body.findElement(DocumentApp.ElementType.TABLE, search)) {
tables.push(search.getElement().asTable());
}
tables.forEach(function (table) {
var rows = table.getNumRows();
// Iterate through each row of the table
for (var r = rows - 1; r >= 0; r--) {
// If the table row contains no text, delete it
if (table.getRow(r).getText().replace(/s/g, "") === "") {
table.removeRow(r);
}
}
});
document.saveAndClose();
}
removeChild method can also be used.
What is the best practice to create unique shareable urls for some text lists users create?
It's a single page website with a content div where users create text lists. Once they click share, how can I store those values inside a shareable url so that another user going to that address loads the same list?
I'm using html, js, jquery, php.
EDIT: as suggested below i'm already saving the lists on a database (firebase), and each have an unique ID, so I'd need to understand how I can create urls with a list id in it, and how to read the url back.
EDIT 2: so this is the code i'm using right now, combining answers from marzelin and the Alchemist Shahed in my other question about my database structure (Firebase how to find child knowing its id but not its parent's id (js)):
//js inside window load function:
const keyOfDynamicHtmlItemRef = new URL(window.location).searchParams.get("share")
if (keyOfDynamicHtmlItemRef) {
var dynamicHtmlListRef = firebase.database().ref('users');
// var dynamicHtmlItemRef = dynamicHtmlListRef.child(keyOfDynamicHtmlItemRef);
// console.log(keyOfDynamicHtmlItemRef);
// dynamicHtmlItemRef.once("value").then(dynamicHtmlSnap => {
// texta.innerHTML = dynamicHtmlSnap.val();
// });
dynamicHtmlListRef.once('value').then((snapshot)=>{
snapshot.forEach(function(data) {
if (data.key == keyOfDynamicHtmlItemRef) {
myVar = data.c;
myContentDiv.innerHTML = myVar;
}
});
});
}
and i'm simply trying to manually write the url in the searchbar as a first step, as https://example.com/?share=<random list id i copied from db>, but it does nothing.
So the way I would to this is I would have the users share click trigger a save to database saving all the dynamically generated content into a table.
One of the table values would be a randomly generated unique identifier of some sort that I would use as a query in the url like https://www.example.org/?share=skd822475
Then when a user visits the site and that query is in the url id use the unique identifier to look up the database and publish the dynamic content back on the page.
I would also put a half life on the database entry's of say no more than 30 days so that it doesn't clog up the db.
Saving data and creating shareable link:
document.querySelector(".share").addEventListener("click" => {
var dynamicHtmlListRef = firebase.database().ref('dynamic_html');
var dynamicHtmlItemRef = dynamicHtmlListRef.push();
dynamicHtmlItemRef.set(userCreatedDynamicHtml);
var keyOfDynamicHtmlItem = dynamicHtmlItemRef.key;
var linkToDynamicHtmlItem = `${window.location}?share=${keyofDynamicHtmlItem}`;
alert(`link: ${linkToDynamicHtmlItem}`)
})
Showing the dynamic HTML based on query parameters:
const keyOfDynamicHtmlItemRef = new URL(window.location).searchParams.get("share")
if (keyOfDynamicHtmlItemRef) {
var dynamicHtmlListRef = firebase.database().ref('dynamic_html');
var dynamicHtmlItemRef = dynamicHtmlListRef.child(keyOfDynamicHtmlItemRef);
keyOfDynamicHtmlItemRef.once("value").then(dynamicHtmlSnap => {
document.querySelector(".dynamic-html-mountpoint").innerHTML = dynamicHtmlSnap.val();
});
}
Let's start with the first question "How to create urls with a list id in it?"
The thing is that to answer this one we need to answer the second question first witch is
"How to read the url back?"
Consider that you have a php page named "draft". when a user visit https://www.example.com/draft?listId=an_id you will get listId using php like so $_GET("listId") and use that value to retrieve the list data and display the page content.
Now coming back to the first question, if the user share the draft like in social media (ex: facebook) then there is no problem because he will share a link and all his followers and any other user can access it easily. but if the user just save the draft then you will have to change the page url dynamically like this window.history.pushState(null, null, '/draft?listId=your_newly_created_id'); and so the user will copy the url and do whatever he wnt with it (sharing it in stackoverflow maybe example using jsfiddle http://jsfiddle.net/F2es9/ (you can change the url to look like this using 'htaccess' file)) at the end I would like to tell you that we don't "create" urls.
Edit
without using php code (or any other server side code). the difference will be in retrieving the data.
instead of using $_GET("listId") you will use new URL(window.location).searchParams.get("listId") to get the list id in javascript then using this value you can retrieve data from firebase and display your content
I'm currently creating a spreadsheet where users can enter an item number and the sheet will return a description and price in the next two columns.
The spreadsheet pulls the item information from another sheet (not another page, rather a whole different URL) and the sheet updates itself every time an item number is entered (no vlookup because the info is on another URL).
Most likely, multiple people will need this form at the same time, and they will also need a copy for reference records. They all have access to the "Master File", the one I have, and I was hoping they could simply make a copy and then fill out the form.
However, while the code in my script works just fine on the Master File, when they make a copy the program won't run. I know it has to do with the triggers not being copied over and I've read up on writing triggers in the script, but here's the problem.
The users cannot see the script - that is, we don't want them to see any code. So they can't go in, turn on triggers via "Resources" or click the run/debug in script editor.
So basically I need a user to be able to open a shared, view only file, make a copy of a spreadsheet (which gets info from another sheet and has triggers), and use that spreadsheet buy inputting item numbers. Most of these people should not be able to see the inner workings, and wouldn't understand any of it anyway.
I was thinking a possible solution would be like what they do in this video around 25:56 or 37:355 where they can press a button and it writes the triggers. They don't go over how to do it though.
If your users are all in a private domain, your best solution would be to publish a private add-on (i.e. available only to domain users). That's not an option if you're using a consumer account.
Alternatively, you can use a menu-driven function to programmatically create the trigger(s) you need. This is effective in your case, because:
The "original" spreadsheet is shared as read-only, and users are expected to make a personal copy to enter their own data.
The users will be owners of their copies, so contained scripts will run as them and for them.
For example, you can try this shared spreadsheet. It's shared public, read-only, but if you save a copy, you will see a Custom Menu that sets a trigger function, and updates the first cell in the spreadsheet. Ten seconds late, the trigger function updates it again.
The demo script is contained in the spreadsheet, so you can see it for yourself there. Here's all it contains:
// Create a menu that will initialize the trigger
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Custom Menu')
.addItem('Initialize spreadsheet', 'setTrigger')
.addToUi();
}
function setTrigger() {
// clear any existing triggers
var triggers = ScriptApp.getUserTriggers(SpreadsheetApp.getActive())
for (var i=0; i<triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
// set new trigger
ScriptApp.newTrigger("runTrigger")
.timeBased()
.after(10*1000) // 10s delay
.create();
announce("Trigger set. Wait for it...");
}
function runTrigger() {
announce("Trigger fired! This completes our demo.");
}
// Update first cell in spreadsheet
function announce(message) {
var range = SpreadsheetApp.getActive().getSheets()[0].getRange("A1");
range.setValue(message);
}
Instead of a menu, you could include a "button" image, and link a script to that. I didn't look at the video, but that's probably what they did. You can see more about that (silly, imho) option in How do you add UI inside cells in a google spreadsheet using app script?
From what I understood you don't need a script here, spreadsheet formulas will do the trick.
to import data from one spreadsheet to an other you can use the formula "importData" and put these data in a hiden sheet.
then you can use "vlookup" formula on this import or even better a "filter" formula (try the filter formula you'll love it).
I'm trying to create a basic time clock web app.
So far, I'm using this script to create this web app which takes the input values and puts them in this spreadsheet for the time stamping part.
I need it to use one of the values from the form and perform a lookup in this sheet (take the longId and find me the name) and return the (name) value to the html page as a verification for the end user that they were identified correctly. Unfortunately, I don't know enough to grasp what I'm doing wrong. Let me know if I need to provide more info.
Edit 1
I'm thinking that I wasn't clear enough. I don't need the user info from entry, I need the user from a lookup. The user will be entering their ID anonymously, I need to match the ID to their info, and bring the info back for them to verify.
Edit 2
Using the link provided by Br. Sayan, I've created this script using this spreadsheet as above to test one piece of this. The web app here spits out: undefined. It should spit out "Student 3" Still not sure what I'm doing wrong.
One way for the next button to grab the student input field:
<input type="submit" onclick="studentName(document.getElementById('student').value)" value="Next..."/>
That sends the value to this func in Javascript.html:
function studentName(value) {
google.script.run
.withSuccessHandler(findSuccess)
.findStudent(value);
}
Which sends it to a findStudent(value) in Code.gs
You do the lookup and the return value goes back to findSuccess( result ) back in Javascript.html. Handle the result from there.
Also consider keeping the stock preventDefault() code that comes with the Web App template in the Help > Welcome Screen.
Please try this one:
(source: technokarak.com)
Also please have a look at:
Retrieve rows from spreadsheet data using GAS
EDIT:
Please make these changes in your function and let us know.
function findValue() {
var data = SpreadsheetApp.openById("15DRZRQ2Hcd7MNnAsu_lnZ6n4kiHeXW_OMPP3squbTLE").getSheetByName("Volatile Data").getDataRange().getValues();
for(i in data) {
if(data[i][3] == 100000003) {
Logger.log("yes");
Logger.log(data[i][0]);
var student = [];
student.push(data[i][0]);
return student;
}
}
}
It is a complicated answer, I have had a lot of success with:
function process(object){
var user = Session.getActiveUser().getEmail();
var key = object.Key;
send(key);
}
function send(k){
var ss =
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastR = ss.GetLastRow();
ss.GetRange(lastR,1).SetValue(k);
}
On your html button you will need to have inside the tags
onClick="google.script.run
.withSuccessHandler(Success)
.process(this.parentNode);"
In order for this to work, obviously you will need to have your fields named accordingly.
Edit: The only thing I did not include in the code was a Success handler, which will be in your html of the GAS script. This should point you in a direction that can resolve that.
Hope this helps.
I want to set up a WooCommerce shop for a WordPress site (which I've never done before by the way).
However, I do not want to use any type of WooCommerce generated pages.
What I want is to add/remove products (along with all other relevant product data), create categories, subcategories etc.. using the WooCommerce "control panel" from inside the Wordpress Dashboard and every time I do that, I want a WooCommerce object (containing all that data) to be updated/generated and made available across all (or some specific) pages.
The reason for that is because I want to build everything myself (product pages, cart page, checkout page, category pages, subcategory pages).
In order to populate these pages with the relevant data, however, I'll need to have access to the whole of WooCommerce data from any of those pages.
Some examples will explain better what I'm trying to achieve. (note : I'll be using Javascript and generating everything on the client)
Case #1
Let's say I am in the "Shop" page, and I want to populate a column with all available product categories. I need to be able to do something like that :
var wooCommerceShopData = getAllWooCommerceShopData(); // an Ajax request that will fetch me all that data
var arrayOfAllAvailableCategories = wooCommerceShopData.categories;
Case #2
Now suppose I am in the "sportswear" category. I need to be able to do something like that :
var wooCommerceShopData = getAllWooCommerceShopData();
var productsObjectForCurrentCategory = wooCommerceShopData.categories["sportswear"].products;
Case #3
If I am on a "sportwear"-category product page, I need to be able to do something like that :
var wooCommerceShopData = getAllWooCommerceShopData();
var currentProductData = wooCommerceShopData.categories["sportswear"].products["BLK123XMENSHORTS"];
var currentProductPrice = currentProductData.price;
// etc......
Is this possible and is there an WooCommerce/Wordpress API for that ?
WooCommerce provides a Rest API.
You can find the documentation here:
https://woocommerce.github.io/woocommerce-rest-api-docs/
Hope it helps.
this is probably the best REST API DOCUMENTATION for Woocommerce.