سرویس HTML: با توابع سرور ارتباط برقرار کنید

google.script.run یک API جاوا اسکریپت ناهمزمان سمت کلاینت است که به صفحات سرویس HTML اجازه می‌دهد تا توابع Apps Script سمت سرور را فراخوانی کنند. مثال زیر اساسی‌ترین عملکرد google.script.run را نشان می‌دهد - فراخوانی یک تابع روی سرور از جاوا اسکریپت سمت کلاینت.

کد.gs

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

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

فهرست.html

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

اگر این اسکریپت را به عنوان یک برنامه وب مستقر کنید و به URL آن مراجعه کنید، چیزی نخواهید دید، اما اگر گزارش‌ها را مشاهده کنید، خواهید دید که تابع سرور doSomething فراخوانی شده است.

فراخوانی‌های سمت کلاینت به توابع سمت سرور ناهمزمان هستند: پس از اینکه مرورگر درخواست می‌کند که سرور تابع doSomething را اجرا کند، مرورگر بلافاصله بدون انتظار برای پاسخ، به خط بعدی کد ادامه می‌دهد. این بدان معناست که فراخوانی‌های تابع سرور ممکن است به ترتیبی که انتظار دارید اجرا نشوند. اگر دو تابع را همزمان فراخوانی کنید، هیچ راهی برای دانستن اینکه کدام تابع ابتدا اجرا می‌شود وجود ندارد. نتیجه ممکن است هر بار که صفحه را بارگذاری می‌کنید متفاوت باشد. در این شرایط، کنترل‌کننده‌های موفقیت و شکست به کنترل جریان کد شما کمک می‌کنند.

رابط برنامه‌نویسی کاربردی google.script.run امکان فراخوانی همزمان ۱۰ تابع سرور را فراهم می‌کند. اگر در حالی که ۱۰ تابع هنوز در حال اجرا هستند، فراخوانی یازدهم را انجام دهید، تابع سرور تا زمانی که یکی از ۱۰ جایگاه آزاد شود، به تأخیر می‌افتد. در عمل، به ندرت باید به این محدودیت فکر کنید، به خصوص که اکثر مرورگرها از قبل تعداد درخواست‌های همزمان به یک سرور را به عددی کمتر از ۱۰ محدود می‌کنند. به عنوان مثال، در فایرفاکس، این محدودیت ۶ است. اکثر مرورگرها به طور مشابه درخواست‌های اضافی سرور را تا زمانی که یکی از درخواست‌های موجود تکمیل شود، به تأخیر می‌اندازند.

پارامترها و مقادیر بازگشتی

فراخوانی یک تابع سرور با پارامترهایی از کلاینت. به طور مشابه، یک تابع سرور می‌تواند مقداری را به عنوان پارامتر ارسالی به یک کنترل‌کننده موفقیت ، به کلاینت بازگرداند.

پارامترهای قانونی و مقادیر بازگشتی، مقادیر اولیه جاوا اسکریپت مانند Number ، Boolean ، String یا null و همچنین اشیاء و آرایه‌های جاوا اسکریپت هستند که از مقادیر اولیه، اشیاء و آرایه‌ها تشکیل شده‌اند. یک عنصر form در صفحه نیز به عنوان یک پارامتر قانونی است، اما باید تنها پارامتر تابع باشد و به عنوان یک مقدار بازگشتی قانونی نیست. اگر سعی کنید یک عنصر Date ، Function ، DOM را علاوه بر یک form یا نوع ممنوعه دیگر، از جمله انواع ممنوعه درون اشیاء یا آرایه‌ها، ارسال کنید، درخواست‌ها با شکست مواجه می‌شوند. اشیاء که ارجاعات دایره‌ای ایجاد می‌کنند نیز با شکست مواجه می‌شوند و فیلدهای تعریف نشده در آرایه‌ها null می‌شوند.

توجه داشته باشید که شیء ارسالی به سرور، یک کپی از شیء اصلی می‌شود. اگر یک تابع سرور، شیء‌ای را دریافت کند و ویژگی‌های آن را تغییر دهد، ویژگی‌های شیء در کلاینت تحت تأثیر قرار نمی‌گیرند.

گردانندگان موفقیت

از آنجا که فراخوانی‌های google.script.run ناهمزمان هستند، کد سمت کلاینت بدون انتظار برای پاسخ، به خط بعدی ادامه می‌دهد. برای مشخص کردن یک تابع فراخوانی که هنگام پاسخ سرور اجرا می‌شود، withSuccessHandler(function) استفاده کنید. اگر تابع سرور مقداری را برگرداند، API آن مقدار را به عنوان پارامتر به تابع فراخوانی ارسال می‌کند.

مثال زیر هنگام پاسخ سرور، یک هشدار مرورگر نمایش می‌دهد. این نمونه کد نیاز به مجوز دارد زیرا تابع سمت سرور به حساب Gmail شما دسترسی دارد. برای مجوزدهی اسکریپت، قبل از بارگذاری صفحه، تابع getUnreadEmails را یک بار به صورت دستی از ویرایشگر اسکریپت اجرا کنید. از طرف دیگر، وقتی برنامه وب را برای اجرا به عنوان "کاربری که به برنامه وب دسترسی دارد" مستقر می‌کنید ، هنگام بارگذاری برنامه از شما درخواست مجوز می‌شود.

کد.gs

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

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

فهرست.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) به شما امکان می‌دهد یک مدیریت‌کننده‌ی خطا را به جای مدیریت‌کننده‌ی موفقیت تعیین کنید. اگر خطایی رخ دهد، API شیء Error را به عنوان یک آرگومان به مدیریت‌کننده‌ی خطا ارسال می‌کند.

به طور پیش‌فرض، اگر یک مدیریت‌کننده‌ی خطا (failure handler) مشخص نکنید، خطاها در کنسول جاوا اسکریپت ثبت می‌شوند. برای لغو این مورد، withFailureHandler(null) را فراخوانی کنید یا یک مدیریت‌کننده‌ی خطا (failure handler) ارائه دهید که هیچ کاری انجام ندهد.

همانطور که این مثال نشان می‌دهد، سینتکس مربوط به کنترل‌کننده‌های شکست تقریباً مشابه کنترل‌کننده‌های موفقیت است.

کد.gs

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

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

فهرست.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 باشند.

در این مثال، کلیک کردن روی هر یک از دو دکمه، آن دکمه را با مقداری از سرور به‌روزرسانی می‌کند در حالی که دکمه‌ی دیگر بدون تغییر باقی می‌ماند، حتی با وجود اینکه هر دو یک کنترل‌کننده‌ی موفقیت (failure handler) مشترک دارند. در داخل کنترل‌کننده‌ی onclick ، کلمه‌ی کلیدی this به خود button اشاره دارد.

کد.gs

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

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

فهرست.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 تبدیل می‌شوند.

این مثال یک فرم، شامل یک فیلد ورودی فایل، را بدون بارگذاری مجدد صفحه پردازش می‌کند؛ فایل را در گوگل درایو آپلود می‌کند و سپس آدرس اینترنتی (URL) فایل را در صفحه سمت کلاینت چاپ می‌کند. در داخل کنترل‌کننده onsubmit ، کلمه کلیدی this به خود فرم اشاره دارد. توجه داشته باشید که پس از بارگذاری همه فرم‌های صفحه، عملکرد ارسال پیش‌فرض توسط preventFormSubmit غیرفعال شده است. این کار از هدایت صفحه به یک آدرس اینترنتی نادرست در صورت بروز استثنا جلوگیری می‌کند.

کد.gs

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

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

فهرست.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();

توابع خصوصی

توابع سرور که نام آنها با زیرخط (_) پایان می‌یابد، خصوصی (private) در نظر گرفته می‌شوند. این توابع را نمی‌توان توسط google.script فراخوانی کرد و نام آنها هرگز برای کلاینت ارسال نمی‌شود. می‌توانید از آنها برای پنهان کردن جزئیات پیاده‌سازی که باید در سرور مخفی نگه داشته شوند، استفاده کنید. google.script همچنین نمی‌تواند توابع درون کتابخانه‌ها یا توابعی را که در سطح بالای اسکریپت تعریف نشده‌اند، ببیند.

در این مثال، تابع getBankBalance در کد کلاینت موجود است؛ کاربری که کد منبع شما را بررسی می‌کند می‌تواند نام آن را حتی اگر آن را فراخوانی نکرده باشید، کشف کند. با این حال، توابع deepSecret_ و obj.objectMethod کاملاً برای کلاینت نامرئی هستند.

کد.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
  }
};

فهرست.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 Docs، Google Sheets یا Forms را می‌توان با فراخوانی متدهای google.script.host به نام‌های setWidth(width) یا setHeight(height) در کد سمت کلاینت تغییر اندازه داد. (برای تنظیم اندازه اولیه یک کادر محاوره‌ای، از متدهای HtmlOutput setWidth(width) و setHeight(height) استفاده کنید.) توجه داشته باشید که کادرهای محاوره‌ای هنگام تغییر اندازه، در مرکز پنجره والد قرار نمی‌گیرند و تغییر اندازه سایدبارها امکان‌پذیر نیست.

بستن پنجره‌های گفتگو و نوارهای کناری در Google Workspace

اگر از سرویس HTML برای نمایش یک کادر محاوره‌ای یا نوار کناری در Google Docs، Sheets یا Forms استفاده می‌کنید، نمی‌توانید با فراخوانی window.close رابط را ببندید. در عوض، باید google.script.host.close را فراخوانی کنید. برای مثال، به بخش مربوط به ارائه HTML به عنوان رابط کاربری Google Workspace مراجعه کنید.

جابجایی فوکوس مرورگر در Google Workspace

برای تغییر فوکوس در مرورگر کاربر از یک کادر محاوره‌ای یا نوار کناری به ویرایشگر Google Docs، Sheets یا Forms، متد google.script.host.editor.focus را فراخوانی کنید. این متد به ویژه در ترکیب با متدهای سرویس Document به نام‌های Document.setCursor(position) و Document.setSelection(range) مفید است.