خدمة HTML: التواصل مع دوال الخادم

google.script.run غير متزامن واجهة برمجة تطبيقات JavaScript من جهة العميل تسمح لصفحات خدمة HTML بطلب البيانات من جهة الخادم وظائف "برمجة تطبيقات Google". يوضح المثال التالي الوظائف الأساسية من google.script.runاستدعاء دالة على الخادم من JavaScript من جهة العميل.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function doSomething() {
  Logger.log('I was called!');
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      google.script.run.doSomething();
    </script>
  </head>
</html>

إذا نشرت هذا النص البرمجي كتطبيق ويب وزرت عنوان URL الخاص به، لن ترى أي شيء، ولكن إذا عرضت السجلات، فسترى أن الخادم يعمل تم الاتصال بـ "doSomething()".

تكون الطلبات من جهة العميل للدوال من جهة الخادم غير متزامنة: بعد فتح المتصفّح. أن يشغّل الخادم الدالة doSomething()، سيستمر المتصفّح مباشرةً إلى السطر التالي من التعليمة البرمجية دون انتظار الرد. يعني ذلك قد لا يتم تنفيذ استدعاءات دالة الخادم بالترتيب الذي تتوقعه. إذا أجريت دعوتَي دالة في الوقت نفسه، لا تتوفّر طريقة لمعرفة الدالة التي سيتم تنفيذها أولاً، وقد تختلف النتيجة في كل مرة تحمّل فيها الصفحة. في هذه الحالة، تساعدك معالجات النجاح ومعالجات الأخطاء في التحكّم في تسلسل رمزك البرمجي.

تسمح واجهة برمجة التطبيقات google.script.run بإجراء 10 طلبات متزامنة لوظائف الخادم. في حال حذف تجري الاتصال الحادي عشر بينما لا تزال الرقم 10 قيد التشغيل، فإن دالة الخادم ستكون متأخرًا حتى يتم تحرير أحد الأماكن العشرة. من الناحية العملية، نادرًا ما يكون لديك للتفكير في هذا القيد، خاصةً وأن معظم المتصفحات تضع حدودًا عدد الطلبات المتزامنة إلى نفس الخادم برقم أقل من 10. في Firefox، على سبيل المثال، الحد الأقصى هو 6. وتؤجل معظم المتصفّحات أيضًا طلبات العميل الزائدة إلى الخادم إلى أن يكتمل أحد الطلبات الحالية.

المَعلمات والقيم المعروضة

يمكنك استدعاء دالة خادم تضم معلمات من العميل. وبالمثل، على إرجاع قيمة إلى العميل كمعلمة تم تمريرها إلى معالج النجاح.

المعلمات القانونية والقيم المعروضة هي قواعد أساسية لـ JavaScript مثل Number Boolean أو String أو null، بالإضافة إلى كائنات JavaScript والصفائف التي تتألف من الوحدات الأساسية والكائنات والصفيفات. عنصر form في الصفحة هي أيضًا قانونية كمعلمة، ولكن يجب أن تكون هي المعلمة الوحيدة للدالة، إلا أنها لا تُعتبر قانونية كقيمة معروضة. تخفق الطلبات إذا حاولت اجتياز Date أو Function أو DOM إلى جانب form أو أي نوع آخر محظور بما في ذلك الأنواع المحظورة داخل الكائنات أو الصفائف. الكائنات التي تنشئ ستفشل المراجع الدائرية أيضًا، وتصبح الحقول غير المحددة داخل الصفائف null

لاحظ أن العنصر الذي يتم تمريره إلى الخادم يصبح نسخة من الأصل. إذا تتلقى دالة الخادم كائنًا وتغير خصائصه، والخصائص على لا يتأثر العميل.

معالِجات النجاح

لأن الرمز من جهة العميل يستمر إلى السطر التالي بدون انتظار الخادم الاتصال لإكمال الطلب، withSuccessHandler(function) تحديد دالة استدعاء من جانب العميل لتشغيلها عندما يستخدم الخادم ويستجيب. إذا أرجعت دالة الخادم قيمة، فستمرر واجهة برمجة التطبيقات هذه القيمة إلى للدالة الجديدة كمعلمة.

يعرض المثال التالي تنبيه متصفح عند استجابة الخادم. ملاحظة أن عينة التعليمات البرمجية هذه تتطلب تفويضًا لأن الدالة من جانب الخادم تدخل إلى حسابك في Gmail. إنّ أبسط طريقة لمصادقة النص البرمجي هي تشغيله الدالة getUnreadEmails() يدويًا من أداة تعديل النصوص البرمجية مرة قبل لتحميل الصفحة. بدلاً من ذلك، عندما نشر تطبيق الويب، يمكنك اختيار لتنفيذه بصفتك "المستخدم الذي يصل إلى تطبيق الويب"، وفي هذه الحالة ستكون ستطلب الحصول على إذن عند تحميل التطبيق.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  return GmailApp.getInboxUnreadCount();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(numUnread) {
        var div = document.getElementById('output');
        div.innerHTML = 'You have ' + numUnread
            + ' unread messages in your Gmail inbox.';
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

معالِجات الأعطال

وفي حالة إخفاق الخادم في الاستجابة أو عرض خطأ، withFailureHandler(function) تحديد معالِج الإخفاق بدلاً من معالِج النجاح، وذلك باستخدام Error كائن (إن وُجد) تم تمريره كوسيطة.

بشكل افتراضي، إذا لم تحدد معالِجًا للإخفاق، يتم تسجيل الإخفاقات في وحدة تحكم JavaScript. لإلغاء هذه المشكلة، يمكنك الاتصال بالرقم withFailureHandler(null) أو طلب المعرّف. أي معالج إخفاق لا يفعل شيئًا.

وتكون بنية معالِجات الإخفاق مماثلة تقريبًا لمعالجات النجاح، حيث مثالنا.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getUnreadEmails() {
  // 'got' instead of 'get' will throw an error.
  return GmailApp.gotInboxUnreadCount();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onFailure(error) {
        var div = document.getElementById('output');
        div.innerHTML = "ERROR: " + error.message;
      }

      google.script.run.withFailureHandler(onFailure)
          .getUnreadEmails();
    </script>
  </head>
  <body>
    <div id="output"></div>
  </body>
</html>

كائنات المستخدم

يمكنك إعادة استخدام معالِج النجاح أو التعطّل نفسه لإجراء مكالمات متعددة إلى الخادم من خلال استدعاء withUserObject(object) لتحديد عنصر سيتم تمريره إلى المعالِج كمَعلمة ثانية. هذا "كائن المستخدم" - يجب عدم الخلط بينه وبين صف User — يتيح لك الرد على السياق الذي اتصل فيه العميل بالخادم. لأنّ كائنات المستخدم يتم إرسالها إلى الخادم، فيمكن أن تكون أي شيء تقريبًا، بما في ذلك الدوال، ونموذج DOM والعناصر، وهكذا، دون القيود على المعلمات والقيم المعروضة لمكالمات الخادم. ومع ذلك، لا يمكن أن تكون كائنات المستخدم كائنات تم إنشاؤها باستخدام new.

في هذا المثال، سيؤدي النقر فوق أي من زرين إلى تحديث هذا الزر قيمة من الخادم مع ترك الزر الآخر بدون تغيير، على الرغم من مشاركة معالج واحد للنجاح. داخل معالج onclick، الكلمة الرئيسية this إلى button نفسها.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getEmail() {
  return Session.getActiveUser().getEmail();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function updateButton(email, button) {
        button.value = 'Clicked by ' + email;
      }
    </script>
  </head>
  <body>
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
    <input type="button" value="Not Clicked"
      onclick="google.script.run
          .withSuccessHandler(updateButton)
          .withUserObject(this)
          .getEmail()" />
  </body>
</html>

النماذج

إذا استدعيت دالة خادم باستخدام عنصر form كمَعلمة، يصبح النموذج كائنًا واحدًا يتضمّن أسماء الحقول كمفاتيح وقيم الحقول كقيم. تتم تحويل كل القيمة إلى سلاسل، باستثناء محتوى حقول input-file التي تصبح كائنات Blob.

يعالج هذا المثال نموذجًا، بما في ذلك حقل إدخال ملف، بدون إعادة تحميل الصفحة، بل يحمّل الملف إلى Google Drive ثم يطبع عنوان URL للملف في صفحة جهة العميل. داخل معالج onsubmit، الكلمة الرئيسية this إلى النموذج نفسه. لاحظ أنه عند تحميل جميع النماذج في الصفحة سيتم إيقاف إجراء الإرسال التلقائي من خلال preventFormSubmit. وهذا يمنع الصفحة من إعادة التوجيه إلى عنوان URL غير دقيق في حالة وجود استثناء.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function processForm(formObject) {
  var formBlob = formObject.myFile;
  var driveFile = DriveApp.createFile(formBlob);
  return driveFile.getUrl();
}

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      // Prevent forms from submitting.
      function preventFormSubmit() {
        var forms = document.querySelectorAll('form');
        for (var i = 0; i < forms.length; i++) {
          forms[i].addEventListener('submit', function(event) {
            event.preventDefault();
          });
        }
      }
      window.addEventListener('load', preventFormSubmit);

      function handleFormSubmit(formObject) {
        google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
      }
      function updateUrl(url) {
        var div = document.getElementById('output');
        div.innerHTML = '<a href="' + url + '">Got it!</a>';
      }
    </script>
  </head>
  <body>
    <form id="myForm" onsubmit="handleFormSubmit(this)">
      <input name="myFile" type="file" />
      <input type="submit" value="Submit" />
    </form>
    <div id="output"></div>
 </body>
</html>

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

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

يمكنك استخدام أي مجموعة وأي ترتيب بقيمة withSuccessHandler(). withFailureHandler() وwithUserObject() يمكنك أيضًا استدعاء أيّ من الدوالّ المعدِّلة في مشغّل النصوص البرمجية الذي سبق أن تم ضبط قيمة له. القيمة تتجاوز القيمة السابقة ببساطة.

يضبط هذا المثال معالج إخفاق شائعًا لجميع اتصالات الخادم الثلاثة، ولكن معالِجات نجاح منفصلة:

var myRunner = google.script.run.withFailureHandler(onFailure);
var myRunner1 = myRunner.withSuccessHandler(onSuccess);
var myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);

myRunner1.doSomething();
myRunner1.doSomethingElse();
myRunner2.doSomething();

الدوال الخاصة

تُعتبر وظائف الخادم التي تنتهي أسماؤها بشرطة سفلية بمثابة دوال خاصة. لا يمكن استدعاء هذه الدوال بواسطة google.script ولا يتم تغيير أسمائها مطلقًا إرسال إلى العميل. ويمكنك بالتالي استخدامها لإخفاء تفاصيل التنفيذ التي يجب الحفاظ على سريتها على الخادم. لا يمكن لـ "google.script" أيضًا الاطّلاع على التفاصيل. الدوال ضمن المكتبات والدوال التي ليست تم الإعلان عنه في المستوى الأعلى من النص البرمجي.

في هذا المثال، تتوفر الدالة getBankBalance() في البرنامج الرمز البرمجي يمكن للمستخدم الذي يفحص رمز المصدر اكتشاف اسمه حتى فلا تسميه. ومع ذلك، تكون الدالتان deepSecret_() وobj.objectMethod() غير مرئية تمامًا لـ للعميل.

Code.gs

function doGet() {
  return HtmlService.createHtmlOutputFromFile('Index');
}

function getBankBalance() {
  var email = Session.getActiveUser().getEmail()
  return deepSecret_(email);
}

function deepSecret_(email) {
 // Do some secret calculations
 return email + ' has $1,000,000 in the bank.';
}

var obj = {
  objectMethod: function() {
    // More secret calculations
  }
};

Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script>
      function onSuccess(balance) {
        var div = document.getElementById('output');
        div.innerHTML = balance;
      }

      google.script.run.withSuccessHandler(onSuccess)
          .getBankBalance();
    </script>
  </head>
  <body>
    <div id="output">No result yet...</div>
  </body>
</html>

تغيير حجم مربّعات الحوار في Google Workspace التطبيقات

مربعات حوار مخصصة في "مستندات Google" أو "جداول بيانات Google" يمكن تغيير حجم النماذج من خلال استدعاء طرق google.script.host setWidth(width) أو setHeight(height) بوصة التعليمات البرمجية من جانب العميل. (لضبط الحجم الأولي لمربّع حوار، استخدِم HtmlOutput الطريقة setWidth(width) و setHeight(height).) يُرجى العِلم أنّ مربّعات الحوار لا تتم إعادة وضعها في منتصف النافذة الرئيسية عند تغيير حجمها، ولا يمكن تغيير حجم أشرطة القوائم الجانبية.

إغلاق مربّعات الحوار والأشرطة الجانبية في Google Workspace

إذا كنت تستخدم خدمة HTML لعرض مربع حوار أو الشريط الجانبي في "مستندات Google" أو "جداول بيانات Google" النماذج، لا يمكنك إغلاق الواجهة من خلال طلب الرقم window.close(). بدلاً من ذلك، عليك الاتصال بالرقم google.script.host.close(). على سبيل المثال، راجع القسم الذي يتناول عرض رمز HTML ك Google Workspace واجهة مستخدم.

جارٍ نقل تركيز المتصفّح في Google Workspace

لتبديل التركيز في متصفّح المستخدم من مربّع حوار أو شريط جانبي إلى أداة تحرير "مستندات Google" أو "جداول بيانات Google" أو "نماذج Google"، ما عليك سوى استدعاء الأسلوب google.script.host.editor.focus(). تكون هذه الطريقة مفيدة بشكل خاص عند استخدامها مع أسلوبَي خدمة المستندات Document.setCursor(position) و Document.setSelection(range).