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

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

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

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

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

طباعة النصوص البرمجية

تؤدي طباعة النصوص البرمجية، التي تستخدم بنية <?= ... ?>، إلى إخراج نتائج الرمز البرمجي في الصفحة باستخدام الهروب السياقي.

يعني إلغاء السياق أنّ "برمجة تطبيقات Google" تتتبّع سياق الإخراج على الصفحة، داخل إحدى سمات 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 بدون تغيير.

رمز "برمجة تطبيقات Google" في النصوص البرمجية

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

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

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

يمكن للنصوص البرمجية استدعاء أي دالة محددة في ملف رمز برمجي لبرمجة تطبيقات Google أو مكتبة. يوضح هذا المثال طريقة واحدة لسحب البيانات من جدول بيانات إلى قالب، ثم إنشاء جدول 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>

الاتصال بواجهات برمجة تطبيقات "برمجة تطبيقات Google" مباشرةً

يمكنك أيضًا استخدام رمز "برمجة تطبيقات Google" مباشرةً في النصوص البرمجية. يحقق هذا المثال نفس النتيجة مثل المثال السابق عن طريق تحميل البيانات في النموذج نفسه بدلاً من تحميل دالة منفصلة.

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" العادي.

إليك النموذج البسيط الذي يعرض قائمة بمنتجات 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 (تم التقييم)

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