एचटीएमएल सेवा: सर्वर के फ़ंक्शन के साथ संपर्क करना

google.script.run एक एसिंक्रोनस क्लाइंट-साइड JavaScript API है. इसकी मदद से, एचटीएमएल सेवा वाले पेज, सर्वर-साइड Apps Script फ़ंक्शन को कॉल कर सकते हैं. यहां दिए गए उदाहरण में, google.script.run की सबसे बुनियादी सुविधा दिखाई गई है. इसमें क्लाइंट-साइड JavaScript से सर्वर पर किसी फ़ंक्शन को कॉल करना शामिल है.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function doSomething() {
  Logger.log('I was called!');
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      google.script.run.doSomething();
    </script>
  </head>
</html>

इस स्क्रिप्ट को वेब ऐप्लिकेशन के तौर पर डिप्लॉय करने और इसके यूआरएल पर जाने पर, आपको कुछ नहीं दिखेगा. हालांकि, लॉग देखने पर आपको पता चलेगा कि सर्वर फ़ंक्शन doSomething को कॉल किया गया था.

सर्वर-साइड फ़ंक्शन के लिए क्लाइंट-साइड कॉल एसिंक्रोनस होते हैं: जब ब्राउज़र, सर्वर से फ़ंक्शन doSomething चलाने का अनुरोध करता है, तो ब्राउज़र जवाब का इंतज़ार किए बिना, कोड की अगली लाइन पर तुरंत चला जाता है. इसका मतलब है कि सर्वर फ़ंक्शन कॉल, आपकी उम्मीद के मुताबिक क्रम में लागू नहीं हो सकते. अगर एक ही समय पर दो फ़ंक्शन कॉल किए जाते हैं, तो यह पता नहीं लगाया जा सकता कि कौनसा फ़ंक्शन पहले चलेगा. इसलिए, हर बार पेज लोड करने पर नतीजे अलग-अलग हो सकते हैं. ऐसी स्थिति में, सक्सेस हैंडलर और गड़बड़ी हैंडलर आपके कोड के फ़्लो को कंट्रोल करने में मदद करते हैं.

google.script.run एपीआई, सर्वर फ़ंक्शन को एक साथ 10 कॉल करने की अनुमति देता है. अगर 10 कॉल अब भी चल रहे हैं और आपने 11वां कॉल किया है, तो सर्वर फ़ंक्शन तब तक डिले हो जाएगा, जब तक 10 में से कोई एक कॉल पूरा नहीं हो जाता. आम तौर पर, आपको इस पाबंदी के बारे में ज़्यादा नहीं सोचना चाहिए. इसकी वजह यह है कि ज़्यादातर ब्राउज़र, एक ही सर्वर पर एक साथ किए जाने वाले अनुरोधों की संख्या को पहले से ही 10 से कम पर सीमित कर देते हैं. उदाहरण के लिए, Firefox में यह सीमा 6 है. ज़्यादातर ब्राउज़र, सर्वर से किए गए अतिरिक्त अनुरोधों को तब तक रोकते हैं, जब तक मौजूदा अनुरोधों में से कोई एक पूरा नहीं हो जाता.

पैरामीटर और दिखाई गई वैल्यू

क्लाइंट से मिले पैरामीटर की मदद से, सर्वर फ़ंक्शन को कॉल करें. इसी तरह, सर्वर फ़ंक्शन, क्लाइंट को वैल्यू दिखा सकता है. इसके लिए, सक्सेस हैंडलर को पास किए गए पैरामीटर का इस्तेमाल किया जाता है.

कानूनी पैरामीटर और रिटर्न वैल्यू, JavaScript प्रिमिटिव होती हैं. जैसे, Number, Boolean, String या null. साथ ही, ये JavaScript ऑब्जेक्ट और ऐसे ऐरे भी होते हैं जो प्रिमिटिव, ऑब्जेक्ट, और ऐरे से बने होते हैं. पेज में मौजूद form एलिमेंट को भी पैरामीटर के तौर पर इस्तेमाल किया जा सकता है. हालांकि, यह फ़ंक्शन का सिर्फ़ एक पैरामीटर होना चाहिए. साथ ही, इसे रिटर्न वैल्यू के तौर पर इस्तेमाल नहीं किया जा सकता. अगर form के अलावा, Date, Function, डीओएम एलिमेंट या अन्य प्रतिबंधित टाइप पास करने की कोशिश की जाती है, तो अनुरोध पूरे नहीं होंगे. इसमें ऑब्जेक्ट या ऐरे के अंदर मौजूद प्रतिबंधित टाइप भी शामिल हैं. सर्कुलर रेफ़रंस बनाने वाले ऑब्जेक्ट भी काम नहीं करते. साथ ही, ऐरे में मौजूद ऐसे फ़ील्ड जिनकी वैल्यू तय नहीं की गई है वे null बन जाते हैं.

ध्यान दें कि सर्वर को पास किया गया ऑब्जेक्ट, ओरिजनल ऑब्जेक्ट की कॉपी बन जाता है. अगर कोई सर्वर फ़ंक्शन किसी ऑब्जेक्ट को स्वीकार करता है और उसकी प्रॉपर्टी बदलता है, तो क्लाइंट पर मौजूद प्रॉपर्टी पर इसका कोई असर नहीं पड़ता.

सक्सेस हैंडलर

google.script.run कॉल एसिंक्रोनस होते हैं. इसलिए, क्लाइंट-साइड कोड जवाब का इंतज़ार किए बिना अगली लाइन पर चला जाता है. सर्वर से जवाब मिलने पर चलने वाले कॉलबैक फ़ंक्शन के बारे में बताने के लिए, withSuccessHandler(function) का इस्तेमाल करें. अगर सर्वर फ़ंक्शन कोई वैल्यू दिखाता है, तो एपीआई उस वैल्यू को पैरामीटर के तौर पर कॉलबैक फ़ंक्शन को पास करता है.

यहां दिए गए उदाहरण में, सर्वर के जवाब देने पर ब्राउज़र में दिखने वाली सूचना दिखाई गई है. इस कोड सैंपल के लिए अनुमति ज़रूरी है, क्योंकि सर्वर-साइड फ़ंक्शन आपके Gmail खाते को ऐक्सेस करता है. स्क्रिप्ट को अनुमति देने के लिए, पेज लोड करने से पहले स्क्रिप्ट एडिटर से getUnreadEmails फ़ंक्शन को मैन्युअल रूप से एक बार चलाएं. इसके अलावा, जब "वेब ऐप्लिकेशन ऐक्सेस करने वाले उपयोगकर्ता" के तौर पर काम करने के लिए, वेब ऐप्लिकेशन को डिप्लॉय किया जाता है, तब ऐप्लिकेशन लोड करते समय आपसे अनुमति मांगी जाती है.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  return GmailApp.getInboxUnreadCount();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(numUnread) {
        var div = document.getElementById('output');
        div.innerHTML = 'You have ' + numUnread
            + ' unread messages in your Gmail inbox.';
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

फ़ेलियर हैंडलर

अगर सर्वर जवाब नहीं देता है या कोई गड़बड़ी दिखाता है, तो withFailureHandler(function) आपको गड़बड़ी हैंडल करने वाले फ़ंक्शन को सेट करने की सुविधा देता है, ताकि उसे सफलता हैंडल करने वाले फ़ंक्शन की जगह चलाया जा सके. गड़बड़ी होने पर, एपीआई Error ऑब्जेक्ट को गड़बड़ी हैंडलर के आर्ग्युमेंट के तौर पर पास करता है.

डिफ़ॉल्ट रूप से, अगर आपने गड़बड़ी हैंडलर नहीं चुना है, तो गड़बड़ियों को JavaScript कंसोल में लॉग किया जाता है. इसे बदलने के लिए, withFailureHandler(null) को कॉल करें या ऐसा फ़ेलियर हैंडलर दें जो कुछ न करे.

फ़ेल होने पर काम करने वाले हैंडलर का सिंटैक्स, सफल होने पर काम करने वाले हैंडलर के सिंटैक्स जैसा ही होता है. इस उदाहरण में दिखाया गया है.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  // 'got' instead of 'get' throws an error.
  return GmailApp.gotInboxUnreadCount();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onFailure(error) {
        var div = document.getElementById('output');
        div.innerHTML = "ERROR: " + error.message;
      }

      google.script.run.withFailureHandler(onFailure)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

उपयोगकर्ता ऑब्जेक्ट

अगर आपको सर्वर को किए गए कई कॉल के लिए, सफलता या गड़बड़ी के बारे में सूचना देने वाले एक ही हैंडलर का दोबारा इस्तेमाल करना है, तो withUserObject(object) को कॉल करें. इससे, एक ऐसा ऑब्जेक्ट तय किया जा सकेगा जिसे हैंडलर को दूसरे पैरामीटर के तौर पर पास किया जाता है. इस "उपयोगकर्ता ऑब्जेक्ट" को User क्लास के साथ भ्रमित नहीं किया जाना चाहिए. इससे आपको उस कॉन्टेक्स्ट का जवाब देने में मदद मिलती है जिसमें क्लाइंट ने सर्वर से संपर्क किया था. उपयोगकर्ता ऑब्जेक्ट को सर्वर पर नहीं भेजा जाता है. इसलिए, ये ऑब्जेक्ट कुछ भी हो सकते हैं. जैसे, फ़ंक्शन और डीओएम एलिमेंट. इन पर सर्वर कॉल के लिए, पैरामीटर और रिटर्न वैल्यू से जुड़ी पाबंदियां लागू नहीं होती हैं. उपयोगकर्ता ऑब्जेक्ट, new ऑपरेटर का इस्तेमाल करके बनाए गए ऑब्जेक्ट नहीं हो सकते.

इस उदाहरण में, दोनों में से किसी भी बटन पर क्लिक करने से, सर्वर से मिली वैल्यू के साथ बटन अपडेट हो जाता है. हालांकि, दोनों बटन एक ही सक्सेस हैंडलर शेयर करते हैं, लेकिन दूसरे बटन में कोई बदलाव नहीं होता. onclick हैंडलर के अंदर, this कीवर्ड button को दिखाता है.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getEmail() {
  return Session.getActiveUser().getEmail();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function updateButton(email, button) {
        button.value = 'Clicked by ' + email;
      }
    </script>
  </head>
  <body>
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
  </body>
</html>

फ़ॉर्म

अगर किसी सर्वर फ़ंक्शन को form एलिमेंट के साथ पैरामीटर के तौर पर कॉल किया जाता है, तो फ़ॉर्म एक ऑब्जेक्ट बन जाता है. इसमें फ़ील्ड के नाम, कुंजियों के तौर पर और फ़ील्ड की वैल्यू, वैल्यू के तौर पर होती हैं. फ़ाइल-इनपुट फ़ील्ड के कॉन्टेंट को छोड़कर, सभी वैल्यू को स्ट्रिंग में बदल दिया जाता है. फ़ाइल-इनपुट फ़ील्ड का कॉन्टेंट, Blob ऑब्जेक्ट बन जाता है.

इस उदाहरण में, फ़ॉर्म को प्रोसेस किया जाता है. इसमें फ़ाइल-इनपुट फ़ील्ड भी शामिल है. हालांकि, पेज को फिर से लोड नहीं किया जाता. यह फ़ाइल को Google Drive पर अपलोड करता है. इसके बाद, क्लाइंट-साइड पेज में फ़ाइल का यूआरएल प्रिंट करता है. onsubmit हैंडलर के अंदर, this कीवर्ड फ़ॉर्म को दिखाता है. ध्यान दें कि पेज पर सभी फ़ॉर्म लोड होने पर, preventFormSubmit ने सबमिट करने की डिफ़ॉल्ट कार्रवाई बंद कर दी है. इससे पेज को किसी अपवाद की स्थिति में, गलत यूआरएल पर रीडायरेक्ट होने से रोका जा सकता है.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function processForm(formObject) {
  var formBlob = formObject.myFile;
  var driveFile = DriveApp.createFile(formBlob);
  return driveFile.getUrl();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      // Prevent forms 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);

      function handleFormSubmit(formObject) {
        google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
      }
      function updateUrl(url) {
        var div = document.getElementById('output');
        div.innerHTML = '<a href="' + url + '">Got it!</a>';
      }
    </script>
  </head>
  <body>
    <form id="myForm" onsubmit="handleFormSubmit(this)">
      <input name="myFile" type="file" />
      <input type="submit" value="Submit" />
    </form>
    <div id="output"></div>
 </body>
</html>

स्क्रिप्ट रनर

google.script.run को "स्क्रिप्ट रनर" के लिए बिल्डर के तौर पर इस्तेमाल किया जा सकता है. अगर स्क्रिप्ट रनर में सफलता हैंडलर, गड़बड़ी हैंडलर या उपयोगकर्ता ऑब्जेक्ट जोड़ा जाता है, तो मौजूदा रनर में कोई बदलाव नहीं होता. इसके बजाय, आपको नई स्क्रिप्ट रनर मिलती है, जिसमें नया व्यवहार होता है.

withSuccessHandler, withFailureHandler, और withUserObject को किसी भी क्रम में और किसी भी कॉम्बिनेशन में इस्तेमाल करें. इसके अलावा, स्क्रिप्ट रनर पर वैल्यू में बदलाव करने वाले किसी भी फ़ंक्शन को कॉल करें. नई वैल्यू, पिछली वैल्यू को बदल देती है.

इस उदाहरण में, तीनों सर्वर कॉल के लिए एक सामान्य गड़बड़ी हैंडलर सेट किया गया है. हालांकि, सफलता के लिए दो अलग-अलग हैंडलर सेट किए गए हैं:

var myRunner = google.script.run.withFailureHandler(onFailure);
var myRunner1 = myRunner.withSuccessHandler(onSuccess);
var myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);

myRunner1.doSomething();
myRunner1.doSomethingElse();
myRunner2.doSomething();

निजी फ़ंक्शन

जिन सर्वर फ़ंक्शन के नाम के आखिर में अंडरस्कोर होता है उन्हें निजी माना जाता है. इन फ़ंक्शन को google.script से कॉल नहीं किया जा सकता. साथ ही, इनके नाम कभी भी क्लाइंट को नहीं भेजे जाते. इनका इस्तेमाल, लागू करने की ऐसी जानकारी को छिपाने के लिए किया जा सकता है जिसे सर्वर पर गोपनीय रखना ज़रूरी है. google.script को लाइब्रेरी में मौजूद फ़ंक्शन या स्क्रिप्ट के टॉप लेवल पर एलान न किए गए फ़ंक्शन भी नहीं दिखते.

इस उदाहरण में, फ़ंक्शन getBankBalance क्लाइंट कोड में उपलब्ध है. अगर कोई व्यक्ति आपके सोर्स कोड की जांच करता है, तो उसे इसका नाम पता चल सकता है. भले ही, आपने इसे कॉल न किया हो. हालांकि, क्लाइंट को deepSecret_ और obj.objectMethod फ़ंक्शन नहीं दिखते.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getBankBalance() {
  var email = Session.getActiveUser().getEmail()
  return deepSecret_(email);
}

function deepSecret_(email) {
 // Do some secret calculations
 return email + ' has $1,000,000 in the bank.';
}

var obj = {
  objectMethod: function() {
    // More secret calculations
  }
};

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(balance) {
        var div = document.getElementById('output');
        div.innerHTML = balance;
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getBankBalance();
    </script>
  </head>
  <body>
    <div id="output">No result yet...</div>
  </body>
</html>

Google Workspace ऐप्लिकेशन में डायलॉग बॉक्स का साइज़ बदलना

Google Docs, Google Sheets या Forms में कस्टम डायलॉग बॉक्स का साइज़ बदला जा सकता है. इसके लिए, क्लाइंट-साइड कोड में google.script.host तरीके setWidth(width) या setHeight(height) को कॉल करें. (डायलॉग का शुरुआती साइज़ सेट करने के लिए, HtmlOutput तरीकों setWidth(width) और setHeight(height) का इस्तेमाल करें.) ध्यान दें कि साइज़ बदलने पर, डायलॉग पैरंट विंडो में फिर से बीच में नहीं आते. साथ ही, साइडबार का साइज़ नहीं बदला जा सकता.

Google Workspace में डायलॉग बॉक्स और साइडबार बंद करना

अगर Google Docs, Sheets या Forms में डायलॉग या साइडबार दिखाने के लिए, HTML सेवा का इस्तेमाल किया जाता है, तो window.close को कॉल करके इंटरफ़ेस को बंद नहीं किया जा सकता. इसके बजाय, आपको google.script.host.close को कॉल करना होगा. उदाहरण के लिए, Google Workspace के उपयोगकर्ता इंटरफ़ेस के तौर पर एचटीएमएल को दिखाने के बारे में सेक्शन देखें.

Google Workspace में ब्राउज़र फ़ोकस को एक जगह से दूसरी जगह ले जाना

उपयोगकर्ता के ब्राउज़र में, फ़ोकस को डायलॉग या साइडबार से वापस Google Docs, Sheets या Forms एडिटर पर ले जाने के लिए, google.script.host.editor.focus तरीके को कॉल करें. यह तरीका, Document service के Document.setCursor(position) और Document.setSelection(range) तरीकों के साथ इस्तेमाल करने पर ज़्यादा फ़ायदेमंद होता है.