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

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

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

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

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

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

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

में ब्राउज़र फ़ोकस को मूव करना

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