Multiple Forms, One Project [duplicate]

Solution 1:

Multi Page Web App

This is working basic four page webapp. It only displays the page title and four buttons on the left sidebar for navigation.

Code.gs:

function getScriptURL(qs) {
  var url = ScriptApp.getService().getUrl();
  //Logger.log(url + qs);
  return url + qs ;
}

function doGet(e) 
{
  //Logger.log('query params: ' + Utilities.jsonStringify(e));
  if(e.queryString !=='')
  {  
    switch(e.parameter.mode)
    {
      case 'page4':
        setPage('Page4')      
        return HtmlService
        .createTemplateFromFile('Page4')
        .evaluate()
        .addMetaTag('viewport', 'width=device-width, initial-scale=1')
        .setTitle("Page4"); 
        break;  
      case 'page3':
        setPage('Page3');        
        return HtmlService
        .createTemplateFromFile('Page3')
        .evaluate()
        .addMetaTag('viewport', 'width=device-width, initial-scale=1')
        .setTitle("Page3");
        break;
      case 'page2':
        setPage('Page2');        
        return HtmlService
        .createTemplateFromFile('Page2')
        .evaluate()
        .addMetaTag('viewport', 'width=device-width, initial-scale=1')
        .setTitle("Page2");
        break;  
      case 'page1':
         setPage('Page1');
         return HtmlService
        .createTemplateFromFile('Page1')
        .evaluate()
        .addMetaTag('viewport', 'width=device-width, initial-scale=1')
        .setTitle("Page1");
        break;
      default:
        setPage('Page1');
        return HtmlService
        .createTemplateFromFile('Page1')
        .evaluate()
        .addMetaTag('viewport', 'width=device-width, initial-scale=1')
        .setTitle("Page1");
        break;
    }
  }
  else
  {
    setPage('Page1');
    return HtmlService
    .createTemplateFromFile('Page1')
    .evaluate()
    .addMetaTag('viewport', 'width=device-width, initial-scale=1')
    .setTitle("Page1");
  }
}

function getPageData()
{
  var s='';
  s+='<input type="button" value="Page1" onClick="getUrl(\'?mode=page1\');" />';
  s+='<br /><input type="button" value="Page2" onClick="getUrl(\'?mode=page2\');" />';
  s+='<br /><input type="button" value="Page3" onClick="getUrl(\'?mode=page3\');" />';
  s+='<br /><input type="button" value="Page4" onClick="getUrl(\'?mode=page4\');" />';
  var rObj={menu:s,title:getPage()};
  Logger.log(rObj);
  return rObj;
}

function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}

Pages.gs

I decided to switch to user scripts properties service since you may want to have more that one user. I often develop scripts that only I use.

function setPage(page) {
  var ps=PropertiesService.getUserProperties();
  ps.setProperty('PageTitle', page);
  return ps.getProperty('PageTitle');
}

function initPage() {
  var ps=PropertiesService.getUserProperties();
  ps.setProperty('PageTitle','');
  return ps.getProperty('PageTitle');
}

function getPage() {
  var ps=PropertiesService.getUserProperties();
  var pt=ps.getProperty('PageTitle');
  return pt;
}

globals.gs:

I was using this for the page title but decided that some of you may wish to have multiple users and so user properties service is a more logical selection. I'll probably use this for something else so I left it in. But your call.

function getGlobals(){
  var ss=SpreadsheetApp.getActive();
  var sh=ss.getSheetByName('Globals');
  var rg=sh.getRange(1,1,getGlobalHeight(),2);
  var vA=rg.getValues();
  var g={};
  for(var i=0;i<vA.length;i++){
    g[vA[i][0]]=vA[i][1];
  }
  return g;
}

function setGlobals(dfltObj){
  if(dfltObj){
    var ss=SpreadsheetApp.getActive();
    var sh=ss.getSheetByName('Globals');
    var rg=sh.getRange(1,1,getGlobalHeight(),2);
    var vA=rg.getValues();
    for(var i=0;i<vA.length;i++){
      vA[i][1]=dfltObj[vA[i][0]];
    }
    rg.setValues(vA);
  }
}

function getGlobal(key) {
  var rObj=getGlobals();
  if(rObj.hasOwnProperty(key)){
    return rObj[key];
  }else{
    throw(Utilities.formatString('JJE-SimpleUtilitiesScripts-Error: Globals does not contain a key named %s.',key));
  }  
}

function setGlobal(key,value){
  var curObj=getGlobals();
  curObj[key]=value;
  setGlobals(curObj);
}

function getGlobalHeight(){
  var ss=SpreadsheetApp.getActive();
  var sh=ss.getSheetByName('Globals');
  var rg=sh.getRange(1,1,sh.getMaxRows(),1);
  var vA=rg.getValues();
  for(var i=0;i<vA.length;i++){
    if(!vA[i][0]){
      break;
    }
  }
  return i;
}

Page1.html:

1 of 4. They start out the same. I make them this way and if I need to customize them I'll usually just do it right inside the html page. But it's also possible to add additional javascript or css pages to support variations from page to page.

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <?!= include('res') ?>
    <?!= include('css') ?>
  </head>
  <body>
    <?!= include('content') ?>
    <?!= include('script') ?>
  </body>
</html>
<html><head>

content.html:

    <div class="sidenav"></div>
    <div class="header">
      <h1 id="ttl"></h1>
    </div>
    <div class="main"></div>

script.html:

<script>
  $(function(){
    google.script.run
    .withSuccessHandler(updatePageData)
    .getPageData();
  });
    function getUrl(qs){
      google.script.run
      .withSuccessHandler(loadNewPage)
      .getScriptURL(qs);
    }
     function updatePageData(dObj){
      $('.sidenav').html(dObj.menu);
      $('.header #ttl').html(dObj.title);
    }
    function loadNewPage(url){
      window.open(url,"_top");
    }
    console.log('script.html');
</script>

css.html:

A very simple styles page.

<style>
input[type="button"],input[type="text"],label{margin:2px 2px 5px 5px;}
body {
  background-color:#fbd393;
  font-family: "Lato", sans-serif;
}
.sidenav {
  height: 100%;
  width: 75px;
  position: fixed;
  z-index: 1;
  top: 0;
  left: 0;
  background-color: #EEE;
  overflow-x: hidden;
  padding-top: 5px;
}

.sidenav a {
  padding: 6px 8px 6px 16px;
  text-decoration: none;
  font-size: 16px;
  color: #818181;
  display: block;
}
.sidenav a:hover,label:hover {
  color: #770000;
}
.header{
  margin-left: 75px; /* Same as the width of the sidenav */
  font-size: 16px; 
  padding: 0px 5px;
  background-color:#fbd393;
  height:60px;
}

.main {
  margin-left: 75px; /* Same as the width of the sidenav */
  font-size: 16px; /* Increased text to enable scrolling */
  padding: 0px 5px;
  background-color:#e9e8de;
  height:450px;
}

@media screen and (max-height: 450px) {
  .sidenav {padding-top: 5px;}
  .sidenav a {font-size: 16px;}
}

</style>

res.html:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

Animation:

enter image description here