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

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

نصوص برمجية

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

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

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

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

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

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

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

موقع 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>

سجل (تقييم)

(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('');
})();

getCodeWithالتعليقات()

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