title: How to pass a parameter to html?
body: <p>I have a script that uses the file picker but I need to pass a specific parameter which is called <code>userId</code> and is kept as a global variable in the calling script. As the calls are asynchronous it seems I cannot access this parameter. Is there a way to access the parameter from the html file or pass this parameter to the html?</p>
  <p>I might be mixing <a href="https://developers.google.com/apps-script/guides/html/templates" rel="nofollow noreferrer">templated html</a> and <a href="https://developers.google.com/apps-script/guides/html/" rel="nofollow noreferrer">non templated</a>.</p>
  <p>Here is the calling code (initiated through a menu item in a spreadsheet):</p>
  <pre><code>function syncStudentsFile(userId, ss) {
    scriptUser_(userId);  // save userId
    Logger.log('SRSConnect : syncStudentsFile : userId:'+userId);  // userId is correct here
    var ss = SpreadsheetApp.getActiveSpreadsheet();  
    var html = HtmlService.createHtmlOutputFromFile('PickerSync.html')
      .setWidth(600).setHeight(425);
    SpreadsheetApp.getUi().showModalDialog(html, 'Select a file');
  }
  
  function scriptUser_(userId) {
    if (userId !== undefined)
      sUserId = userId; // Global variable
    try { return sUserId; } catch (e) { return undefined; }
  }
  
  function getOAuthToken() {  // used by Picker
    DriveApp.getRootFolder();
    return ScriptApp.getOAuthToken();
  }
  </code></pre>
  <p>Here is the html picker file:</p>
  <pre><code>&lt;link rel=&quot;stylesheet&quot; href=&quot;https://ssl.gstatic.com/docs/script/css/add-ons.css&quot;&gt;
  
  &lt;script type=&quot;text/javascript&quot;&gt;
    var DEVELOPER_KEY = '..............';
    var DIALOG_DIMENSIONS = {width: 600, height: 425};
    var pickerApiLoaded = false;
  
    /**
     * Loads the Google Picker API.
     */
    gapi.load('picker', {'callback': function() {
      pickerApiLoaded = true;
    }});
  
    /**
     * Gets the user's access token from the server-side script so that
     * it can be passed to Picker. This technique keeps Picker from needing to
     * show its own authorization dialog, but is only possible if the OAuth scope
     * that Picker needs is available in Apps Script. Otherwise, your Picker code
     * will need to declare its own OAuth scopes.
     */
    function getOAuthToken() {
      google.script.run.withSuccessHandler(createPicker)
          .withFailureHandler(showError).getOAuthToken();
    }
  
    /**
     * Creates a Picker that can access the user's spreadsheets. This function
     * uses advanced options to hide the Picker's left navigation panel and
     * default title bar.
     *
     * @param {string} token An OAuth 2.0 access token that lets Picker access the
     *     file type specified in the addView call.
     */
    function createPicker(token) {
      if (pickerApiLoaded &amp;&amp; token) {
        var uploadView = new google.picker.DocsUploadView();
        var picker = new google.picker.PickerBuilder()
            // Instruct Picker to display only spreadsheets in Drive. For other
            // views, see https://developers.google.com/picker/docs/#otherviews
            .addView(google.picker.ViewId.DOCS)
            .addView(google.picker.ViewId.RECENTLY_PICKED)
            .addView(uploadView)
            .hideTitleBar()
            .setOAuthToken(token)
            .setDeveloperKey(DEVELOPER_KEY)
            .setCallback(pickerCallback)
            // Instruct Picker to fill the dialog, minus 2 pixels for the border.
            .setSize(DIALOG_DIMENSIONS.width - 2,
                DIALOG_DIMENSIONS.height - 2)
            .build();
        picker.setVisible(true);
      } else {
        showError('Unable to load the file picker.');
      }
    }
  
    /**
     * A callback function that extracts the chosen document's metadata from the
     * response object. For details on the response object, see
     * https://developers.google.com/picker/docs/result
     *
     * @param {object} data The response object.
     */
    function pickerCallback(data) {
      var action = data[google.picker.Response.ACTION];
      if (action == google.picker.Action.PICKED) {
        var doc = data[google.picker.Response.DOCUMENTS][0];
        var id = doc[google.picker.Document.ID];
        google.script.host.close();
        // --------------&gt; user global parameter sUserId set earlier
        google.script.run.PickerSyncFile(sUserId, id);
      } else if (action == google.picker.Action.CANCEL) {
        google.script.host.close();
      }
    }
  
    /**
     * Displays an error message within the #result element.
     *
     * @param {string} message The error message to display.
     */
    function showError(message) {
      document.getElementById('result').innerHTML = 'Error: ' + message;
    }
  &lt;/script&gt;
  
  &lt;div&gt;
    &lt;script&gt;getOAuthToken()&lt;/script&gt;
    &lt;p id='result'&gt;&lt;/p&gt;
    &lt;input type=&quot;button&quot; value=&quot;Close&quot; onclick=&quot;google.script.host.close()&quot; /&gt;
  &lt;/div&gt;
  </code></pre>
  <p>Here is the picker code:</p>
  <pre><code>function pickerSyncFile(userId, id) {
    Logger.log('userId:'+userId);  // BUG: it is null
    Logger.log('id:'+id);  // id returned well from picker
  
    // rest of code here but userId was is incorrect
  }
  </code></pre>