एचटीएमएल सेवा: टेंप्लेट वाला एचटीएमएल

टेंप्लेट का इस्तेमाल करके, Google Apps Script कोड और एचटीएमएल को मिक्स किया जा सकता है. इससे, कम मेहनत में डाइनैमिक पेज बनाए जा सकते हैं. अगर आपने PHP, ASP या JSP जैसी टेंप्लेटिंग भाषाओं का इस्तेमाल किया है, जिनमें कोड और एचटीएमएल को मिक्स किया जाता है, तो सिंटैक्स आपको जाना-पहचाना लगेगा.

स्क्रिप्टलेट

Apps Script टेंप्लेट में, स्क्रिप्टलेट नाम के तीन खास टैग हो सकते हैं. स्क्रिप्टलेट में, कोई भी ऐसा कोड लिखा जा सकता है जो सामान्य Apps Script फ़ाइल में काम करता है. स्क्रिप्टलेट, कोड की अन्य फ़ाइलों में तय किए गए फ़ंक्शन को कॉल कर सकते हैं, ग्लोबल वैरिएबल का रेफ़रंस दे सकते हैं या Apps Script के किसी भी एपीआई का इस्तेमाल कर सकते हैं. स्क्रिप्टलेट में फ़ंक्शन और वैरिएबल भी तय किए जा सकते हैं. हालांकि, कोड की फ़ाइलों या अन्य टेंप्लेट में तय किए गए फ़ंक्शन, इन्हें कॉल नहीं कर सकते.

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

Code.gs

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

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    Hello, World! The time is <?= new Date() ?>.
  </body>
</html>

ध्यान दें कि टेंप्लेट वाले एचटीएमएल के लिए doGet फ़ंक्शन, सामान्य एचटीएमएल बनाने और दिखाने के उदाहरणों से अलग होता है. यहां दिखाया गया फ़ंक्शन, एचटीएमएल फ़ाइल से HtmlTemplate ऑब्जेक्ट जनरेट करता है. इसके बाद, स्क्रिप्टलेट को चलाने और टेंप्लेट को HtmlOutput ऑब्जेक्ट में बदलने के लिए, इसके evaluate तरीके को कॉल करता है. स्क्रिप्ट, इस ऑब्जेक्ट को उपयोगकर्ता को दिखा सकती है.

स्टैंडर्ड स्क्रिप्टलेट

स्टैंडर्ड स्क्रिप्टलेट, <? ... ?> सिंटैक्स का इस्तेमाल करते हैं. ये पेज पर कॉन्टेंट को साफ़ तौर पर आउटपुट किए बिना कोड चलाते हैं. हालांकि, जैसा कि इस उदाहरण में दिखाया गया है, स्क्रिप्टलेट में मौजूद कोड का नतीजा, स्क्रिप्टलेट के बाहर मौजूद एचटीएमएल कॉन्टेंट पर अब भी असर डाल सकता है:

Code.gs

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

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? if (true) { ?>
      <p>This is always served!</p>
    <? } else  { ?>
      <p>This is never served.</p>
    <? } ?>
  </body>
</html>

प्रिंट स्क्रिप्टलेट, जो <?= ... ?> सिंटैक्स का इस्तेमाल करते हैं, कॉन्टेक्चुअल एस्केपिंग का इस्तेमाल करके, अपने कोड के नतीजों को पेज पर आउटपुट करते हैं.

कॉन्टेक्चुअल एस्केपिंग का मतलब है कि Apps Script, पेज पर आउटपुट के कॉन्टेक्स्ट को ट्रैक करता है. जैसे, एचटीएमएल एट्रिब्यूट के अंदर, क्लाइंट-साइड script टैग के अंदर या कहीं और. साथ ही, क्रॉस-साइट स्क्रिप्टिंग (XSS) हमलों से बचाने के लिए, एस्केप कैरेक्टर अपने-आप जोड़ता है.

इस उदाहरण में, पहला प्रिंटिंग स्क्रिप्टलेट सीधे तौर पर एक स्ट्रिंग को आउटपुट करता है. इसके बाद, एक स्टैंडर्ड स्क्रिप्टलेट है जो एक कलेक्शन और लूप सेट अप करता है. इसके बाद, कलेक्शन के कॉन्टेंट को आउटपुट करने के लिए, एक और प्रिंटिंग स्क्रिप्टलेट है.

Code.gs

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

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <?= 'My favorite Google products:' ?>
    <? var data = ['Gmail', 'Docs', 'Android'];
      for (var i = 0; i < data.length; i++) { ?>
        <b><?= data[i] ?></b>
    <? } ?>
  </body>
</html>

ध्यान दें कि प्रिंटिंग स्क्रिप्टलेट, सिर्फ़ अपने पहले स्टेटमेंट की वैल्यू को आउटपुट करता है. बाकी स्टेटमेंट ऐसे काम करते हैं जैसे वे किसी स्टैंडर्ड स्क्रिप्टलेट में मौजूद हों. इसलिए, उदाहरण के लिए, स्क्रिप्टलेट <?= 'Hello, world!'; 'abc' ?> सिर्फ़ "Hello, world!" प्रिंट करता है.

फ़ोर्स-प्रिंटिंग स्क्रिप्टलेट

फ़ोर्स-प्रिंटिंग स्क्रिप्टलेट, जो <?!= ... ?> सिंटैक्स का इस्तेमाल करते हैं, वे प्रिंटिंग स्क्रिप्टलेट की तरह होते हैं, हालांकि उनमें कॉन्टेक्चुअल एस्केपिंग नहीं होती.

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

आम तौर पर, फ़ोर्स-प्रिंटिंग स्क्रिप्टलेट के बजाय प्रिंटिंग स्क्रिप्टलेट का इस्तेमाल करें. ऐसा तब तक करें, जब तक आपको यह न पता हो कि आपको एचटीएमएल या JavaScript को बिना किसी बदलाव के प्रिंट करना है.

स्क्रिप्टलेट में Apps Script कोड

स्क्रिप्टलेट, सामान्य JavaScript चलाने तक ही सीमित नहीं हैं. अपने टेंप्लेट को Apps Script डेटा का ऐक्सेस देने के लिए, इन तीन तकनीकों में से किसी का भी इस्तेमाल किया जा सकता है.

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

किसी टेंप्लेट से Apps Script फ़ंक्शन को कॉल करना

स्क्रिप्टलेट, Apps Script कोड फ़ाइल या लाइब्रेरी में तय किए गए किसी भी फ़ंक्शन को कॉल कर सकते हैं. इस उदाहरण में, स्प्रेडशीट से डेटा को टेंप्लेट में लाने और फिर डेटा से एचटीएमएल टेबल बनाने का एक तरीका दिखाया गया है.

Code.gs

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

function getData() {
  return SpreadsheetApp
      .openById('1234567890abcdefghijklmnopqrstuvwxyz')
      .getActiveSheet()
      .getDataRange()
      .getValues();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? var data = getData(); ?>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

Apps Script के एपीआई को सीधे तौर पर कॉल करना

स्क्रिप्टलेट में, Apps Script कोड का सीधे तौर पर भी इस्तेमाल किया जा सकता है. इस उदाहरण में, अलग फ़ंक्शन के बजाय टेंप्लेट में ही डेटा लोड करके, पिछले उदाहरण जैसा ही नतीजा मिलता है.

Code.gs

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

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? var data = SpreadsheetApp
        .openById('1234567890abcdefghijklmnopqrstuvwxyz')
        .getActiveSheet()
        .getDataRange()
        .getValues(); ?>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

वैरिएबल को टेंप्लेट में पुश करना

आखिर में, वैरिएबल को ऑब्जेक्ट की प्रॉपर्टी के तौर पर असाइन करके, उन्हें टेंप्लेट में पुश किया जा सकता है.HtmlTemplate एक बार फिर, इस उदाहरण में पिछले उदाहरणों जैसा ही नतीजा मिलता है.

Code.gs

function doGet() {
  var t = HtmlService.createTemplateFromFile('Index');
  t.data = SpreadsheetApp
      .openById('1234567890abcdefghijklmnopqrstuvwxyz')
      .getActiveSheet()
      .getDataRange()
      .getValues();
  return t.evaluate();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

टेंप्लेट में गड़बड़ियां ठीक करना

टेंप्लेट में गड़बड़ियां ठीक करना मुश्किल हो सकता है, क्योंकि आपके लिखे कोड को सीधे तौर पर नहीं चलाया जाता. इसके बजाय, सर्वर आपके टेंप्लेट को कोड में बदलता है. इसके बाद, उस कोड को चलाता है.

अगर यह साफ़ नहीं है कि टेंप्लेट आपके स्क्रिप्टलेट को कैसे इंटरप्रेट कर रहा है, तो दो गड़बड़ियां ठीक करने के तरीके HtmlTemplate क्लास में आपकी मदद कर सकते हैं यह बेहतर ढंग से समझने में कि क्या हो रहा है.

getCode फ़ंक्शन

The getCode फ़ंक्शन एक स्ट्रिंग दिखाता है. इसमें वह कोड होता है जिसे सर्वर, टेंप्लेट से बनाता है. अगर कोड को लॉग किया जाता है और फिर स्क्रिप्ट एडिटर में चिपकाया जाता है, तो इसे सामान्य Apps Script कोड की तरह चलाया और इसमें मौजूद गड़बड़ियां ठीक की जा सकती हैं.

यहां वह टेंप्लेट दिया गया है जो Google के प्रॉडक्ट की सूची फिर से दिखाता है. इसके बाद, getCode का नतीजा दिया गया है:

Code.gs

function myFunction() {
  Logger.log(HtmlService
      .createTemplateFromFile('Index')
      .getCode());
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <?= 'My favorite Google products:' ?>
    <? var data = ['Gmail', 'Docs', 'Android'];
      for (var i = 0; i < data.length; i++) { ?>
        <b><?= data[i] ?></b>
    <? } ?>
  </body>
</html>

LOG (EVALUATED)

(function() { var output = HtmlService.initTemplate(); output._ =  '<!DOCTYPE html>\n';
  output._ =  '<html>\n' +
    '  <head>\n' +
    '    <base target=\"_top\">\n' +
    '  </head>\n' +
    '  <body>\n' +
    '    '; output._$ =  'My favorite Google products:' ;
  output._ =  '    ';  var data = ['Gmail', 'Docs', 'Android'];
        for (var i = 0; i < data.length; i++) { ;
  output._ =  '        <b>'; output._$ =  data[i] ; output._ =  '</b>\n';
  output._ =  '    ';  } ;
  output._ =  '  </body>\n';
  output._ =  '</html>';
  /* End of user code */
  return output.$out.append('');
})();

getCodeWithComments फ़ंक्शन

getCodeWithComments फ़ंक्शन, getCode() जैसा ही है. हालांकि, यह इंटरप्रेट किए गए कोड को टिप्पणियों के तौर पर दिखाता है. ये टिप्पणियां, ओरिजनल टेंप्लेट के साथ-साथ दिखती हैं.

इंटरप्रेट किए गए कोड को समझना

इंटरप्रेट किए गए कोड के किसी भी सैंपल में, आपको सबसे पहले HtmlService.initTemplate तरीके से बनाया गया, इंप्लिसिट output ऑब्जेक्ट दिखेगा. इस तरीके के बारे में कोई दस्तावेज़ मौजूद नहीं है, क्योंकि इसका इस्तेमाल सिर्फ़ टेंप्लेट को करना होता है. output एक खास HtmlOutput ऑब्जेक्ट है. इसकी दो प्रॉपर्टी के नाम अलग होते हैं: _ और _$. ये append और appendUntrusted को कॉल करने के शॉर्टहैंड हैं.

output की एक और खास प्रॉपर्टी है: $out. यह एक सामान्य HtmlOutput ऑब्जेक्ट को रेफ़र करती है, जिसमें ये खास प्रॉपर्टी नहीं होती हैं. टेंप्लेट, कोड के आखिर में उस सामान्य ऑब्जेक्ट को दिखाता है.

अब आपको यह सिंटैक्स समझ आ गया होगा. इसलिए, अब बाकी कोड को समझा जा सकता है. HTML स्क्रिप्टलेट के बाहर मौजूद कॉन्टेंट (जैसे, b टैग) को output._ = का इस्तेमाल करके जोड़ा जाता है. इसमें कॉन्टेक्चुअल एस्केपिंग नहीं होती. वहीं, स्क्रिप्टलेट को JavaScript के तौर पर जोड़ा जाता है. इसमें कॉन्टेक्चुअल एस्केपिंग हो भी सकती है और नहीं भी. यह स्क्रिप्टलेट के टाइप पर निर्भर करता है.

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

टिप्पणियों की हैरारकी

इंटरप्रेट किए गए कोड में लाइन नंबर बने रहते हैं. इसलिए, स्क्रिप्टलेट में मौजूद टिप्पणियां, अन्य स्क्रिप्टलेट और यहां तक कि एचटीएमएल कोड को भी टिप्पणी के तौर पर दिखा सकती हैं. इन उदाहरणों में, टिप्पणियों के कुछ चौंकाने वाले असर दिखाए गए हैं:

<? var x; // a comment ?> This sentence won't print because a comment begins
inside a scriptlet on the same line.

<? var y; // ?> <?= "This sentence won't print because a comment begins inside
a scriptlet on the same line.";
output.append("This sentence prints because it's on the next line, even though
it's in the same scriptlet.") ?>

<? doSomething(); /* ?>
This entire block is commented out,
even if you add a */ in the HTML
or in a <script> */ </script> tag,
<? until you end the comment inside a scriptlet. */ ?>