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

google.script.run एक एसिंक्रोनस क्लाइंट-साइड JavaScript API है. इसकी मदद से एचटीएमएल-सेवा पेज, सर्वर साइड ऐप्लिकेशन स्क्रिप्ट फ़ंक्शन को कॉल कर सकते हैं. इस उदाहरण में, 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 में यह सीमा छह है. ज़्यादातर ब्राउज़र इसी तरह, ज़्यादा सर्वर अनुरोधों को तब तक रोककर रखते हैं, जब तक कि मौजूदा अनुरोधों में से कोई एक पूरा नहीं हो जाता.

पैरामीटर और रिटर्न वैल्यू

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

कानूनी पैरामीटर और रिटर्न वैल्यू, Number, Boolean, String या null जैसे JavaScript प्रिमिटिव होते हैं. साथ ही, JavaScript ऑब्जेक्ट और अरे भी होते हैं, जो प्रिमिटिव, ऑब्जेक्ट, और अरे से बने होते हैं. पेज में मौजूद एक form एलिमेंट, पैरामीटर के तौर पर भी कानूनी है. हालांकि, यह फ़ंक्शन का ही पैरामीटर होना चाहिए. यह रिटर्न वैल्यू के तौर पर कानूनी नहीं है. अगर form या पाबंदी वाले अन्य टाइप के अलावा, Date, Function, DOM एलिमेंट को पास करने की कोशिश की जाती है, तो अनुरोध नहीं भेजे जा सकते. इनमें, ऑब्जेक्ट या अरे में मौजूद पाबंदी वाले टाइप भी शामिल हैं. गोल आकार वाले रेफ़रंस बनाने वाले ऑब्जेक्ट भी काम करना बंद कर देंगे. साथ ही, अरे में तय नहीं किए गए फ़ील्ड 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 या फ़ॉर्म के कस्टम डायलॉग बॉक्स का साइज़ बदलने के लिए, क्लाइंट-साइड कोड में google.script.host तरीके setWidth(width) या setHeight(height) का इस्तेमाल करें. (किसी डायलॉग का शुरुआती साइज़ सेट करने के लिए, HtmlOutput तरीकों setWidth(width) और setHeight(height) का इस्तेमाल करें.) ध्यान दें कि डायलॉग का साइज़ बदलने पर, डायलॉग पैरंट विंडो में फिर से सेंटर नहीं होते. साथ ही, साइडबार का साइज़ नहीं बदला जा सकता.

डायलॉग और साइडबार को Google Workspaceमें बंद किया जा रहा है

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

ब्राउज़र के फ़ोकस को Google Workspaceमें ले जाया जा रहा है

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