Sharing code between client and server and using libraries with html service

Normally when you write code for apps that have a server and client component, you write the code for one or the other. The client javascript code goes in an html file (usually called something like main.js.html), the mark up goes in a file like index.html and the style code goes in a file called something like style.css.html. The server side code goes in script files called something like namespace.gs

However, it's actually very easy to write client side code as a .gs file, which gives you better editing capabilities since you are using the script editor to edit JavaScript, rather than the script editor to edit JavaScript inside an Html files. It also allows you to use the same code on both the client and the server side!

All you need to do is to is avoid writing any executing code that's specific to either the client or server. This is very straightforward if you are using namespaces - and you should for many reasons.

Using libraries

Once you've established this pattern of organizing your script files, you'll find you can also use the code from libraries, so for example, the idea of a house style, using a style file from a library, means that you can avoid repetitive coding and maintain the look and feel between all your webapps.

How ?

Here's the index.html of a complex application. It is invoked from the server like this
  return HtmlService
  .createTemplateFromFile('index.html')
  .evaluate()
  .setSandboxMode(HtmlService.SandboxMode.IFRAME)
  .setTitle('BigQuiz ')
  .addMetaTag('viewport', 'width=device-width, initial-scale=1');

index.html
<!DOCTYPE html>
<html>
  <head>
  <base target="_top">
    <?!= cHouse.Include.html(['cdncss']); ?>  
  </head>
  <body>
    <?!= cHouse.Include.css(['house']); ?>     
    <?!= Include.html(['app']); ?>  
    
    <?!= cHouse.Include.html(['cdn']) ?>
    <?!= cHouse.Include.gs(['Provoke']); ?>
    
    <?!= Include.js(['main']); ?>
    <?!= Include.gs(['App','Client','Render']); ?>

  </body>
</html>

I have a library called cHouse in which I keep all my shared code, styling and markup for webApps.  It also contains this namespace Include, which needs to be present in any library that you want to pull in client side code from. If you are pulling code from your local app, you need this there too.
Include.gs
/**
 *used to include code in htmloutput
 *@nameSpace Include
 */
var Include = (function (ns) {
  
  /**
  * given an array of .gs file names, it will get the source and return them concatenated for insertion into htmlservice
  * like this you can share the same code between client and server side, and use the Apps Script IDE to manage your js code
  * @param {string[]} scripts the names of all the scripts needed
  * @return {string} the code inside script tags
  */
  ns.gs =  function (scripts) {
    return '<script>\n' + scripts.map (function (d) {
      // getResource returns a blob
      return ScriptApp.getResource(d).getDataAsString();
    })
    .join('\n\n') + '</script>\n';
  };

  /**
  * given an array of .html file names, it will get the source and return them concatenated for insertion into htmlservice
  * @param {string[]} scripts the names of all the scripts needed
  * @param {string} ext file extendion
  * @return {string} the code inside script tags
  */
  ns.html = function (scripts, ext) {
    return  scripts.map (function (d) {
      return HtmlService.createHtmlOutputFromFile(d+(ext||'')).getContent();
    })
    .join('\n\n');
  };
  
  /**
  * given an array of .html file names, it will get the source and return them concatenated for insertion into htmlservice
  * inserts css style
  * @param {string[]} scripts the names of all the scripts needed
  * @return {string} the code inside script tags
  */
  ns.js = function (scripts) {
    return '<script>\n' + ns.html(scripts,'.js') + '</script>\n';
  };
  
  /**
  * given an array of .html file names, it will get the source and return them concatenated for insertion into htmlservice
  * like this you can share the same code between client and server side, and use the Apps Script IDE to manage your js code
  * @param {string[]} scripts the names of all the scripts needed
  * @return {string} the code inside script tags
  */
  ns.css = function (scripts) {
    return '<style>\n' + ns.html(scripts,'.css') + '</style>\n';
  };
  

  return ns;
})(Include || {});

Each method of the Include namespace takes an array of names. These are the names of script files to be inserted. 

Include.html

Inserts the code from any mark up files without modification. This can be used for html code and for including external css or script links. For example since I have an html file in my cHouse library called 'cdn' , I can include these script files in all my webapps using
     <?!= cHouse.Include.html(['cdn']) ?>

<script src="//cdn.muicss.com/mui-0.5.1/js/mui.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/progressbar.js/1.0.0/progressbar.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/es6-promise/3.2.1/es6-promise.min.js"></script>

My main app html code is included from the local project like this

    <?!= Include.html(['app']); ?>  

Include.gs

This includes code that has been written in the gs script editor, but is intended to be run client side (or shared between client and server), so this example will include the given 3 script files from my project to be run client side.

   <?!= Include.gs(['App','Client','Render']); ?>

and this will pull the given script file from my cHouse library which is shared across multiple webapps.

    <?!= cHouse.Include.gs(['Provoke']); ?>

Include.js

This includes js code that has been written in html files, 
    <?!= Include.js(['main']); ?>

In this example app, my main.js.html looks like this
window.addEventListener("load", function () {

  // set up the app
  App.init();

  // get the player data
  Client.register();
  
  // get the data
  Client.provokeCategories();
  
});

Include.css

This is to include style files. Here, I'm using a standard style file, house.css.html from my cHouse library. 

<?!= cHouse.Include.css(['house']); ?> 






For more like this, see Google Apps Scripts snippets. Why not join our forumfollow the blog or follow me on twitter to ensure you get updates when they are available. 




Comments