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

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 बन जाएंगे.

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

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

क्लाइंट-साइड कोड, सर्वर कॉल के पूरा होने का इंतज़ार किए बिना अगली लाइन पर चला जाता है. इसलिए, 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' will throw 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, 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).