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

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

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

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

नीचे दिए गए उदाहरण को स्क्रिप्ट एडिटर में चिपकाने पर, <?= ... ?> टैग (प्रिंटिंग स्क्रिप्टलेट) का कॉन्टेंट इटैलिक फ़ॉर्मैट में दिखेगा. इटैलिक कोड, उपयोगकर्ता को पेज दिखाने से पहले सर्वर पर चलाया जाता है. स्क्रिप्टलेट कोड, पेज दिखाए जाने से पहले काम करता है. इसलिए, यह हर पेज पर सिर्फ़ एक बार चल सकता है. क्लाइंट-साइड JavaScript या Apps स्क्रिप्ट फ़ंक्शन के उलट, 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 ऑब्जेक्ट जनरेट करता है. इसके बाद, स्क्रिप्टलेट चलाने के लिए इसके evaluate() तरीके को कॉल करता है और टेंप्लेट को HtmlOutput ऑब्जेक्ट में बदल देता है, जिसे स्क्रिप्ट उपयोगकर्ता को दिखा सके.

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

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

Code.gs

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

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? if (true) { ?>
      <p>This will always be served!</p>
    <? } else  { ?>
      <p>This will never be 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' ?> सिर्फ़ "नमस्ते, दुनिया!" प्रिंट करता है.

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

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

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

सामान्य नियम के तौर पर, फ़ोर्स-प्रिंटिंग स्क्रिप्टलेट के बजाय प्रिंटिंग स्क्रिप्ट का इस्तेमाल करें. ऐसा तब तक करें, जब तक कि आपको पता न हो कि आपको एचटीएमएल या 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 API को सीधे कॉल किया जा रहा है

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()

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>

लॉग (जांचा गया)

(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 ऑब्जेक्ट के बारे में बताती है जिसमें ये खास प्रॉपर्टी नहीं होती हैं. टेंप्लेट, कोड के आखिर में वह सामान्य ऑब्जेक्ट दिखाता है.

अब आपको इस सिंटैक्स को समझने के बाद, बाकी के कोड आसानी से मिल जाएंगे. स्क्रिप्टलेट के बाहर, एचटीएमएल कॉन्टेंट (जैसे, 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 will print 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. */ ?>