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

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 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' ?> सिर्फ़ "Hello, world!" प्रिंट करता है

स्क्रिप्टलेट को ज़बरदस्ती प्रिंट करना

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

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

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

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

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

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

किसी टेंप्लेट से 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>

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

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