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

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

نصوص برمجية

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

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

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

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>

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