سرویس HTML: HTML الگو

می توانید کد Apps Script و HTML را با هم ترکیب کنید تا صفحات پویا را با حداقل تلاش تولید کنید. اگر از زبان قالبی استفاده کرده اید که کد و HTML را با هم ترکیب می کند، مانند PHP، ASP یا JSP، سینتکس باید به نظر آشنا باشد.

اسکریپت ها

قالب‌های Apps Script می‌توانند شامل سه تگ خاص به نام اسکریپت باشند. در داخل یک اسکریپت، می‌توانید هر کدی را بنویسید که در یک فایل Apps Script معمولی کار می‌کند: اسکریپت‌ها می‌توانند توابع تعریف‌شده در فایل‌های کد دیگر را فراخوانی کنند، به متغیرهای سراسری ارجاع دهند یا از هر یک از Apps Script API استفاده کنند. حتی می‌توانید توابع و متغیرها را در اسکریپت‌ها تعریف کنید، با این نکته که آنها را نمی‌توان با توابع تعریف‌شده در فایل‌های کد یا سایر قالب‌ها فراخوانی کرد.

اگر مثال زیر را در ویرایشگر اسکریپت بچسبانید، محتویات تگ <?= ... ?> (یک اسکریپت چاپی ) به صورت مورب ظاهر می شود. کدهای مورب قبل از اینکه صفحه به کاربر ارائه شود روی سرور اجرا می شود. از آنجایی که کد اسکریپت قبل از ارائه صفحه اجرا می شود، فقط می تواند یک بار در هر صفحه اجرا شود. برخلاف توابع جاوا اسکریپت سمت کلاینت یا 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() برای HTML قالبی با نمونه هایی برای ایجاد و ارائه HTML پایه متفاوت است. تابع نشان داده شده در اینجا یک شی HtmlTemplate را از فایل HTML تولید می کند، سپس متد evaluate() آن را برای اجرای اسکریپت ها و تبدیل قالب به یک شی HtmlOutput که اسکریپت می تواند به کاربر ارائه کند، فراخوانی می کند.

اسکریپت های استاندارد

اسکریپت های استاندارد که از نحو <? ... ?> ، کد را بدون خروجی صریح محتوا به صفحه اجرا کنید. با این حال، همانطور که این مثال نشان می دهد، نتیجه کد داخل یک اسکریپت همچنان می تواند بر محتوای HTML خارج از اسکریپت تأثیر بگذارد:

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 زمینه خروجی را در صفحه ردیابی می کند - داخل یک ویژگی HTML، داخل یک برچسب 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' ?> فقط "سلام، دنیا!"

اسکریپت های اجباری چاپ

اسکریپت‌های چاپ اجباری، که از نحو <?!= ... ?> استفاده می‌کنند، مانند اسکریپت‌های چاپی هستند با این تفاوت که از فرار زمینه‌ای اجتناب می‌کنند.

اگر اسکریپت شما اجازه ورود نامطمئن کاربر را بدهد، فرار متنی مهم است. در مقابل، اگر خروجی اسکریپت شما عمداً حاوی HTML یا اسکریپت‌هایی باشد که می‌خواهید دقیقاً همانطور که مشخص شده درج کنید، باید چاپ اجباری کنید.

به عنوان یک قاعده کلی، از اسکریپت های چاپی به جای اسکریپت های اجباری استفاده کنید، مگر اینکه بدانید که باید HTML یا جاوا اسکریپت را بدون تغییر چاپ کنید.

کد برنامه‌های اسکریپت در اسکریپت‌ها

اسکریپت‌ها به اجرای معمولی جاوا اسکریپت محدود نمی‌شوند. همچنین می توانید از یکی از سه تکنیک زیر برای دسترسی الگوهای خود به داده های Apps Script استفاده کنید.

با این حال، به یاد داشته باشید که از آنجایی که کد قالب قبل از ارائه صفحه به کاربر اجرا می شود، این تکنیک ها فقط می توانند محتوای اولیه را به صفحه وارد کنند. برای دسترسی تعاملی به داده های Apps Script از یک صفحه، به جای آن از google.script.run API استفاده کنید.

فراخوانی توابع Apps Script از یک الگو

اسکریپت‌ها می‌توانند هر تابعی را که در فایل یا کتابخانه کد Apps Script تعریف شده است، فراخوانی کنند. این مثال یک راه برای کشیدن داده ها از یک صفحه گسترده به یک الگو، سپس ساخت یک جدول HTML از داده ها را نشان می دهد.

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>

تماس مستقیم با APIهای 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()

getCode() رشته ای حاوی کدی را که سرور از قالب ایجاد می کند برمی گرداند. اگر کد را وارد کنید ، سپس آن را در ویرایشگر اسکریپت جای‌گذاری کنید، می‌توانید آن را اجرا کرده و مانند کد نرم‌افزار اسکریپت نرم‌افزاری آن را رفع اشکال کنید .

در اینجا الگوی ساده ای است که لیستی از محصولات 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() است، اما کد ارزیابی شده را به عنوان نظراتی که در کنار الگوی اصلی ظاهر می شوند، برمی گرداند.

عبور از کد ارزیابی شده

اولین چیزی که در هر یک از نمونه‌های کد ارزیابی شده مشاهده می‌کنید، شی output ضمنی است که توسط متد HtmlService.initTemplate() ایجاد شده است. این روش بدون سند است زیرا فقط خود الگوها باید از آن استفاده کنند. output یک شیء خاص HtmlOutput با دو ویژگی نام‌های غیرمعمول، _ و _$ است که برای فراخوانی append() و appendUntrusted() هستند.

output یک ویژگی خاص دیگر دارد، $out ، که به یک شیء معمولی HtmlOutput اشاره می کند که این ویژگی های خاص را ندارد. الگو آن شی معمولی را در انتهای کد برمی گرداند.

اکنون که این نحو را فهمیدید، دنبال کردن بقیه کدها باید نسبتاً آسان باشد. محتوای HTML خارج از اسکریپت‌ها (مانند تگ b ) با استفاده از output._ = (بدون فرار متنی )، و اسکریپت‌ها به عنوان جاوا اسکریپت (با یا بدون فرار متنی، بسته به نوع اسکریپت) اضافه می‌شوند.

توجه داشته باشید که کد ارزیابی شده شماره خطوط را از الگو حفظ می کند. اگر هنگام اجرای کد ارزیابی شده خطایی دریافت کردید، خط با محتوای معادل در قالب مطابقت دارد.

سلسله مراتب نظرات

از آنجایی که کد ارزیابی شده اعداد خطوط را حفظ می کند، این امکان برای نظرات درون اسکریپت ها وجود دارد که سایر اسکریپت ها و حتی کدهای HTML را نظر دهند. این مثال‌ها چند اثر شگفت‌انگیز نظرات را نشان می‌دهند:

<? 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. */ ?>