خدمة HTML: نموذج HTML

يمكنك الجمع بين رمز "برمجة تطبيقات Google" وHTML لإنتاج صفحات ديناميكية بأقل جهد. إذا كنت قد استخدمت لغة نماذج تمزج بين الرموز وHTML، مثل PHP أو ASP أو JSP، من المفترض أن تكون البنية مألوفة لك.

النصوص البرمجية الصغيرة

يمكن أن تحتوي نماذج "برمجة تطبيقات Google" على ثلاث علامات خاصة تُعرف باسم "النصوص البرمجية". داخل النص البرمجي الصغير، يمكنك كتابة أي رمز برمجي يمكن استخدامه في ملف برمجة تطبيقات Google عادي: يمكن للنصوص البرمجية الصغيرة استدعاء الدوال المحدّدة في ملفات الرموز البرمجية الأخرى، أو الإشارة إلى المتغيّرات الشاملة، أو استخدام أي من واجهات برمجة التطبيقات في Apps Script. يمكنك أيضًا تعريف الدوال والمتغيّرات ضمن نصوص التشغيل البرمجي الصغيرة، مع العِلم أنّه لا يمكن استدعاؤها بواسطة الدوال المحدّدة في ملفات الرموز البرمجية أو النماذج الأخرى.

في حال لصق المثال أدناه في محرِّر النصوص البرمجية، ستظهر محتويات علامة <?= ... ?> (نص برمجي للطباعة) بخط مائل. يتم تنفيذ هذا الرمز المائل على الخادم قبل عرض الصفحة على المستخدم. ولأنّ رمز JavaScript يتم تنفيذه قبل عرض الصفحة، لا يمكن تشغيله إلا مرة واحدة في كل صفحة. وعلى عكس دوال JavaScript من جهة العميل أو "برمجة تطبيقات Google" التي تستدعيها من خلال 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() ل ejecutant وحدات النصوص البرمجية وتحويل النموذج إلى عنصر 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 أو JavaScript بدون تغيير.

رمز Apps Script في النصوص البرمجية الصغيرة

لا تقتصر النصوص البرمجية على تشغيل JavaScript العادي، يمكنك أيضًا استخدام أي من الأساليب الثلاثة التالية لمنح النماذج إمكانية الوصول إلى بيانات برمجة التطبيقات.

ومع ذلك، تذكَّر أنّه بسبب تنفيذ رمز النموذج قبل عرض الصفحة على المستخدم، لا يمكن لهذه الأساليب إلا عرض المحتوى الأوّلي على الصفحة. للوصول إلى data Apps Script من صفحة بشكل تفاعلي، استخدِم واجهة برمجة التطبيقات google.script.run بدلاً من ذلك.

استدعاء دوالّ "برمجة تطبيقات Google" من نموذج

يمكن للوحدات النصية الصغيرة استدعاء أي دالة محدّدة في ملف رمز أو مكتبة 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>

استدعاء واجهات برمجة تطبيقات 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 مرة أخرى، followed by the result of 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()، لكنّها تعرض الرمز البرمجي المقيّم كتعليقات تظهر جنبًا إلى جنب مع النموذج الأصلي.

الاطّلاع على الرمز الذي تم تقييمه

إنّ أول شيء سيلاحظه في أي من نموذجي الرمز البرمجي المقيّم هو الكائن output الضمني الذي تم إنشاؤه باستخدام الطريقة HtmlService.initTemplate(). هذه الطريقة غير موثَّقة لأنّ النماذج نفسها هي التي تحتاج إلى استخدامها فقط. output هو كائن HtmlOutput خاص يتضمّن سمتَين سميتَين بشكل غير معتاد، وهما _ و_$، وهما اختصار للاتّصال بعلامتَي append() و appendUntrusted().

يحتوي output على سمة خاصة أخرى، وهي $out، والتي تشير إلى عنصر HtmlOutput عادي لا يمتلك هذه السمات الخاصة. يعرض النموذج هذا العنصر العادي في نهاية الرمز.

بعد أن فهمت هذه البنية، من المفترض أن يكون من السهل اتّباع بقية الرمز البرمجي. يتم إلحاق محتوى HTML خارج النصوص البرمجية (مثل علامة b) باستخدام output._ = (بدون تجاوز سياقي)، ويتم إلحاق النصوص البرمجية بلغة JavaScript (مع إهدار سياقي أو بدونه، بناءً على نوع النص البرمجي).

يُرجى العِلم أنّ الرمز الذي تم تقييمه يحافظ على أرقام الأسطر من النموذج. إذا حدث خطأ أثناء تشغيل الرمز الذي تم تقييمه، سيتوافق السطر مع المحتوى المكافئ في النموذج.

التسلسل الهرمي للتعليقات

بما أنّ التعليمات البرمجية التي تم تقييمها تحافظ على أرقام الأسطر، من الممكن أن تؤدي التعليقات داخل نصوص التشغيل النصي إلى تعليق نصوص تشغيل نصية أخرى وحتى رمز 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. */ ?>