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

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

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 API اجازه می دهد تا 10 تماس همزمان با توابع سرور. اگر یازدهمین تماس را در حالی که 10 هنوز در حال اجرا هستند برقرار کنید، عملکرد سرور تا زمانی که یکی از 10 نقطه آزاد شود به تاخیر می افتد. در عمل، به ندرت باید در مورد این محدودیت فکر کنید، به خصوص که اکثر مرورگرها از قبل تعداد درخواست های همزمان را به همان سرور با تعداد کمتر از 10 محدود می کنند. برای مثال، در فایرفاکس، این محدودیت 6 است. اکثر مرورگرها به طور مشابه تاخیر دارند. درخواست های سرور اضافی تا زمانی که یکی از درخواست های موجود تکمیل شود.

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

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

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

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

کنترل کننده های موفقیت

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

مثال زیر یک هشدار مرورگر را هنگام پاسخ دادن سرور نشان می دهد. توجه داشته باشید که این نمونه کد نیاز به مجوز دارد زیرا عملکرد سمت سرور به حساب 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 (در صورت وجود) به عنوان آرگومان ارسال کنید.

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

بستن دیالوگ ها و نوارهای کناری در Google Workspace

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

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

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