How can I get disabled inputs in Post - html

I am using the Google Address Autofill to populate some disabled inputs on the form. The idea is that
The user can see the data I am going to save (because not every field returned by Google is saved).
And they can not interfere with it and give an invalid mismatched set of fields.
Because the inputs are disabled, I cant see them in $POST when the form is submitted. I could mirror each field onto a hidden but enabled input. Is there a cleaner way to do this ?

You could collect them using javascript and add them to the url of the next form and attach it to the button.

You can enable them via javascript when the form is submitted. Give your form an ID, I've called it formId below, and then add the following right before your closing </body> tag
<script>
var form = document.getElementById('formId').addEventListener('submit', function(e) {
e.preventDefault();
var inputs = document.getElementsByTagName('input');
for (var i = 0; i<inputs.length;i++) {
inputs[i].setAttribute('disabled',false);
}
this.submit();
for (var i = 0; i<inputs.length;i++) {
inputs[i].setAttribute('disabled','disabled');
}
});
</script>

Related

How to get data from html from back to serverside code in AppScript

I have search for hours and cant find a solution to this. I have some appscript code that creates a menu and open a modal in google docs:
function showDialog() {
let ui = DocumentApp.getUi();
let html = HtmlService.createHtmlOutputFromFile("dialog");
ui.showModalDialog(html, "dtest");
}
function processForm() {
console.log("test123123123");
}
This works, the modal opens.
In the modal i want to submit a form. For now im just calling a test function when submitting that console.logs to the cloud logger.
<script>
// Prevent form from submitting
function preventFormSubmit() {
var forms = document.querySelectorAll("form");
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener("submit", function (event) {
event.preventDefault();
});
}
}
window.addEventListener("load", preventFormSubmit);
google.script.run.processForm();
</script>
The problem here is that the function processForm() is not triggered.
BUT when i use a doGet() and deploy it as a webapp it works.
How can i get it to work in google docs also? I have tried following the documentation..but i cant get it to work.
Thanks in advance for any help with this.
So i found the problem. I was singed into google with account A, the correct one. And prompts with input and everything worked. In chrome i was signed in with account B. It seems that when you use htmlservice it uses a sandbox, and it uses the user you are logged into chrome with, not the one you are logged into workspace with. So access denined. Use same users both places to solve this problem.

Keep images when adding choices to Google Forms with Apps script

I'm using google scripts to add new choices to a checkbox item in a form every time the form gets submitted. Some of the existing checkbox-items have images attached that users see when filling in the form.
My problem is that every time my script gets run, it removes the images from the checkbox-items. Is there a way to keep the images attached to the form, while adding new choices to it?
(part of) my code:
// retrieve form-checkbox object
var item = form.getItems(FormApp.ItemType.CHECKBOX)
.filter(function(item){
return item.getTitle() === 'Top 5 films';
})[0].asCheckboxItem();
//make 2 lists, 'choices' with all choices in the checkbox obj
// 'existingChoicesTitles' with all the titles of the choices.
var choices = item.getChoices();
var existingChoicesTitles = choices.map(function(value){
return value.getValue();
})
//check if obj in list 'values' already exists in array 'choices, if not add to
//'choices'
for (i = 0; i < values.length; i++) {
if (existingChoicesTitles.includes(values[i]) == false){
choices.push(item.createChoice(values[i]));
}
}
//set 'choices' list as new list of choices
item.setChoices(choices);
Unfortunately there is currently no way to do that.
There is an active Feature Request for the ability to add these kind of images with the Apps Script FormApp.
Add Images to Form Items
I would suggest that you go and mark the ☆ on the issue to let Google know that you would like this functionality, and also to subscribe to updates. You might also want to add in your experience and use case in a comment.
In your case it seems that to add an item to the checkbox list, Apps Script regenerates all the checkbox items, and since FormApp doesn't support these types of images, it doesn't include them when they are regenerated.
For your use case there doesn't seem to be a practical workaround apart from simply not using images in this way if they are to be modified with Apps Script.
If you are willing to put in some extra work, you might want to implement the form as a simple web app. Then you would have almost infinite flexibility and far more functionality.
Web App example
This is a very simple example of a Web App that shows an input box and a button to send the info to the 'back-end', which in this example is Code.gs
Code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("index")
}
function receiveInfo(formResponse) {
Logger.log(formResponse)
}
index.html
<!DOCTYPE html>
<html>
<body>
<input type='text' id='input'></input>
<button id=main>Send Info</button>
<script>
function main(){
const input = document.getElementById('input')
const info = input.value
google.script.run.receiveInfo(info)
}
const mainButton = document.getElementById("main")
mainButton.addEventListener('click', () => main())
</script>
</body>
</html>
References
Feature Request
Web apps
Test a web app deployment
Client-to-server communication

How to dynamically change an existing text item value on Google Forms using Google Apps Script

I have an existing Google Form in which there is a TextItem with a title "Which location was this performed at?".
Whenever the form is loaded (opened), I need to set a location value (loc) to this existing textbox and show it to the user.
function populateMemberIds(loc){
var form = FormApp.openById(formUrl);
var questions = form.getItems();
for (var i=0; i<questions.length; i++){
if(questions[i].getTitle()=="Which location was this performed at?"){
var textItem = questions[i].asTextItem();
//I get stuck here
}
}
I already setup the openForm trigger which allows to run the populateMemberIds function to be run on each form load. Again, what I need is to change the value of the "Your answer" section of the text item with the location value (loc).
I would appreciate any help.
You can't modify a form response filled by a user, you can either create a form response programmatically or edit a response after being submitted. The onOpen form trigger runs when someone opens the form to edit it rather than answer it [1]:
This event does not occur when a user opens a form to respond, but
rather when an editor opens the form to modify it.
Moreover, triggers functions comes with an event parameter already defined [1] so you can't set your own function parameter(s) as you're doing with your loc parameter.
EDIT
You can programmatically create and submit a form response [2], from which you can also get a URL with a prefilled form for the user to finish [3].
function populateMemberIds(loc){
var form = FormApp.openById("[FORM-ID]");
var questions = form.getItems();
var response = form.createResponse();
for (var i=0; i<questions.length; i++){
if(questions[i].getTitle()=="title form"){//Which location was this performed at?"){
var textItem = questions[i].asTextItem();
var itemResponse = textItem.createResponse(loc) ;
response.withItemResponse(itemResponse);
}
}
//Submit programmatically the form response
response.submit();
//URL with prefilled form response
Logger.log(response.toPrefilledUrl());
}
function test () {
populateMemberIds("US");
}
[1] https://developers.google.com/apps-script/guides/triggers/events#google_forms_events
[2] https://developers.google.com/apps-script/reference/forms/form-response
[3] https://developers.google.com/apps-script/reference/forms/form-response#toprefilledurl
The onOpen Google Apps Script triggers (simple and installable) for Google Forms are executed only when the form is opened in the form editor, not when the form is opened by using the view / edit response links.
There are two ways to "prefill" a Google Forms response:
Use the prefilled response URL
Create a response programmatically, then use the editResponseUrl
Related
Is it possible to 'prefill' a google form using data from a google spreadsheet?
How to generate a pre-filled form URL for Google Form

Multiple Page UI using UiService

I would like to use Google Apps Script UiService to produce a multiple page user interface.
Here's what I've got so far:
function doGet(e)
{
var app=UiApp.createApplication();
var nameLabel=app.createLabel('Name:');
var button=app.createButton("next");//my button on clicking,trying to divert to other UI
var handler=app.createServerHandler("myclick");
button.addClickHandler(handler);
app.add(namelabel);
app.add(button);
return app;
}
function myClick(){
//on clicking the button it should call the other ui or other html page
is there any method for that.}
How can I do this?
You should look at How To Allow Users to Review Answers before Submiting Form?, which has an example that does this.
The idea is to create your UiApp with multiple Panels, then show or hide them in response to user actions, using setVisible(). (If you were using the HtmlService, you would enclose your "pages" in different <div>s, and change their display attributes. See toggle show/hide div with button?.)
The Best Practices also describes use of client-side handlers for responsiveness, so let's try that.
/**
* Very simple multiple page UiApp.
*
* This function defines two panels, which appear to the end user
* as separate web pages. Visibility of each panel is set to
* control what the user sees.
*/
function doGet() {
var app = UiApp.createApplication();
var page1 = app.createFlowPanel().setId('page1');
var page2 = app.createFlowPanel().setId('page2');
// Content for Page 1
page1.add(app.createLabel('Page 1'));
var page1Button = app.createButton('Next Page');
page1.add(page1Button);
// Create client handler to "change pages" in browser
var gotoPage2 = app.createClientHandler()
.forTargets(page1).setVisible(false)
.forTargets(page2).setVisible(true);
page1Button.addClickHandler(gotoPage2);
// Content for Page 2
page2.add(app.createLabel('Page 2'));
var page2Button = app.createButton('Previous Page');
page2.add(page2Button);
// Create client handler to "change pages" in browser
var gotoPage1 = app.createClientHandler()
.forTargets(page1).setVisible(true)
.forTargets(page2).setVisible(false);
page2Button.addClickHandler(gotoPage1);
app.add(page1);
app.add(page2);
// Set initial visibility
page1.setVisible(true);
page2.setVisible(false);
return app;
}
That works for changing the view of the UI. To extend this for general purposes, you would likely want to add server-side handlers to the same buttons to perform work, and update the contents of the panels as things progress.
Here is working code
that demonstrates a multiple page form, i.e. it does the initial doGet() and then lets you advance back and forth doing multiple doPost()'s. All this is done in a single getForm() function called by both the standard doGet() and the doPost() functions.
// Muliple page form using Google Apps Script
function doGet(eventInfo) {return GUI(eventInfo)};
function doPost(eventInfo) {return GUI(eventInfo)};
function GUI (eventInfo) {
var n = (eventInfo.parameter.state == void(0) ? 0 : parseInt(eventInfo.parameter.state));
var ui = ((n == 0)? UiApp.createApplication() : UiApp.getActiveApplication());
var Form;
switch(n){
case 0: {
Form = getForm(eventInfo,n); // Use identical forms for demo purpose only
} break;
case 1: {
Form = getForm(eventInfo,n); // In reality, each form would differ but...
} break;
default: {
Form = getForm(eventInfo,n) // each form must abide by (implement) the hidden state variable
} break;
}
return ui.add(Form);
};
function getForm(eventInfo,n) {
var ui = UiApp.getActiveApplication();
// Increment the ID stored in a hidden text-box
var state = ui.createTextBox().setId('state').setName('state').setValue(1+n).setVisible(true).setEnabled(false);
var H1 = ui.createHTML("<H1>Form "+n+"</H1>");
var H2 = ui.createHTML(
"<h2>"+(eventInfo.parameter.formId==void(0)?"":"Created by submission of form "+eventInfo.parameter.formId)+"</h2>");
// Add three submit buttons to go forward, backward and to validate the form
var Next = ui.createSubmitButton("Next").setEnabled(true).setVisible(true);
var Back = ui.createSubmitButton("Back").setEnabled(n>1).setVisible(true);
var Validate = ui.createSubmitButton("Validate").setEnabled(n>0).setVisible(true);
var Buttons = ui.createHorizontalPanel().add(Back).add(Validate).add(Next);
var Body = ui.createVerticalPanel().add(H1).add(H2).add(state).add(Buttons).add(getParameters(eventInfo));
var Form = ui.createFormPanel().setId((n>0?'doPost[':'doGet[')+n+']').add(Body);
// Add client handlers using setText() to adjust state prior to form submission
// NB: Use of the .setValue(val) and .setValue(val,bool) methods give runtime errors!
var onClickValidateHandler = ui.createClientHandler().forTargets(state).setText(''+(parseInt(n)));
var onClickBackHandler = ui.createClientHandler().forTargets(state).setText(''+(parseInt(n)-1));
Validate.addClickHandler(onClickValidateHandler);
Back.addClickHandler(onClickBackHandler);
// Add a client handler executed prior to form submission
var onFormSubmit = ui.createClientHandler()
.forTargets(state).setEnabled(true) // Enable so value gets included in post parameters
.forTargets(Body).setStyleAttribute("backgroundColor","#EEE");
Form.addSubmitHandler(onFormSubmit);
return Form;
}
function getParameters(eventInfo) {
var ui = UiApp.getActiveApplication();
var panel = ui.createVerticalPanel().add(ui.createLabel("Parameters: "));
for( p in eventInfo.parameter)
panel.add(ui.createLabel(" - " + p + " = " + eventInfo.parameter[p]));
return panel;
}
The code uses a single "hidden" state (here visualized in a TextBox) and multiple SubmitButton's to allow the user to advance forward and backward through the form sequence, as well as to validate the contents of the form. The two extra SubmitButton's are "rewired" using ClientHandler's that simply modify the hidden state prior to form submission.
Notes
Note the use of the .setText(value) method in the client handler's. Using the Chrome browser I get weird runtime errors if I switch to either of the TextBox's .setValue(value) or .setValue(value, fireEvents) methods.
I tried (unsuccessfully) to implement this logic using a Script Property instead of the hidden TextBox. Instead of client handlers, this requires using server handlers. The behavior is erratic, suggesting to me that the asynchronous server-side events are occurring after the form submission event.
You could load different UI's on reading the parameters in your app.
The doGet(e) passes the parameters in the app's url. This way you could call your app with for example: ?myapp=1 (url parameter).
in your doGet you could read that parameter with: e.parameter.myapp
This way you could load different applications depending on the parameters that where passed.
You could just change your button with a link (to your own app, with different url parameters).
You could also do it with buttons and handlers but the above way has my preference.
If you want to use a button<>handler just change you main (first panel) and each time add a completely new panel to your app object. This way you would start from scratch (i.e. create a new application).

Dynamically Fill in form from drop down selection

I have the Following Form:
How do i do the following:
When I chose a user from the select user drop down menu, After selecting the user I want to dynamically fill in the data below ? How do I do this without refreshing the page or loading another page?
I am using ASP.NET MVC
I would have to get the User ID from the Select User and then get the appropriate roles from the model
You could use AJAX. Subscribe to the .change event of the DropDown, retrieve the selected value, perform an AJAX call to a controller action sending the selected value which will return as JSON the corresponding list. Then in the success callback of this AJAX call add the necessary information to the lists.
Something along the lines of:
$(function() {
$('#id_of_your_users_ddl').change(function() {
var selectedValue = $(this).val();
var url = $(this).data('url'); // this assumes that you have appended a data-url attribute to your dropdown
$.post(url, { userId: selectedValue }, function(result) {
// result will be a JSON list returned by your controller action
// that you could use here to update your roles lists
});
});
});