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

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

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

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

تسمح واجهة برمجة التطبيقات google.script.run بعشر استدعاءات متزامنة لوظائف الخادم. وإذا أجريت مكالمة الحادية عشرة بينما كان الرقم 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 كمعلمة، يصبح النموذج كائنًا واحدًا يحتوي على أسماء الحقول كمفاتيح وقيم الحقول كقيم. ويتم تحويل جميع القيم إلى سلاسل، باستثناء محتوى حقول إدخال الملفات التي تصبح كائنات 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" من خلال استدعاء طرق google.script.host setWidth(width) أو setHeight(height) في الرمز من جهة العميل. (لضبط الحجم الأولي لمربّع حوار، استخدِم الأسلوبَين HtmlOutput setWidth(width) و setHeight(height)). يُرجى ملاحظة أنّ مربّعات الحوار لا تتم إعادة وضعها في النافذة الرئيسية عند تغيير حجمها، ولا يمكن تغيير حجم الأشرطة الجانبية.

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

إذا كنت تستخدم خدمة HTML لعرض مربّع حوار أو شريط جانبي في "مستندات Google" أو "جداول بيانات 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).