Как показать / скрыть спиннер загрузки в боковой панели аддона, когда диалог выбора Google открыт / закрыт? - PullRequest
0 голосов
/ 01 октября 2019

Я следовал этому руководству: https://developers.google.com/apps-script/guides/dialogs#file-open_dialogs и я могу открыть Google Picker после нажатия кнопки на боковой панели аддона.

Я хочу сделать 2 вещи для лучшего UX для моих пользователей:

  • Во-первых, когда открыт Google Picker, я хочу показать загрузочный счетчик внутри боковой панели аддона (чтобы пользователи не могли взаимодействовать с боковой панелью), чтобы пользователи могли только взаимодействоватьс помощью Google Picker до его закрытия

  • После того, как пользователи выберут документ или напрямую закроют Google Picker, загрузочный счетчик на боковой панели аддона также исчезнет немедленно (мы не должны перезагружать боковую панель), и пользователи могут нормально взаимодействовать с боковой панелью аддона

Обратите внимание: индикатор загрузки внутри боковой панели аддона(не в диалоге выбора)

Я сделал следующее:

У меня есть 2 файла:

  1. sidebar.html
  2. picker.html

BВ этих html-файлах содержатся html, css, js, необходимые для запуска каждого из них.

Внутри sidebar.html, когда мне нужно открыть средство выбора, я позвоню:

function openPicker() {
  // Show spinner here
  showSpinner()

  google.script.run
    .withSuccessHandler(function() { console.log('ok') })
    .showPicker()
}

Выше функции вызовет эту функцию showPicker () в Code.gs:

function showPicker() {
  var html = HtmlService.createHtmlOutputFromFile('picker.html')
    .setWidth(800)
    .setHeight(600)
  FormApp.getUi().showModalDialog(html, 'Select a file')
}

Внутри picker.html у меня есть обратный вызов:

function pickerCallback(data) {
    var action = data[google.picker.Response.ACTION]
    if (action === google.picker.Action.PICKED) {
        var doc = data[google.picker.Response.DOCUMENTS][0]
        var id = doc[google.picker.Document.ID]
        var url = doc[google.picker.Document.URL]
        var title = doc[google.picker.Document.NAME]
        document.getElementById('result').innerHTML =
            '<b>You chose:</b><br>Name: <a href="' + url + '">' + title + '</a><br>ID: ' + id
    } else if (action === google.picker.Action.CANCEL) {
        google.script.host.close()
    }

    // This is a good place to hide the spinner, 
    // but this is picker.html, so it can not control code in sidebar.html to hide the spinner
}

Проблема здесь в том,sidebar.html может знать только, когда показывать счетчик, а не когда закрывать его.

Логика определения момента, когда мы должны закрыть счетчик, находится в самом файле picker.html (только пользователь знает, когда пользовательвыбрал документ или отменил средство выбора).

И я не знаю, как передать эту логику в sidebar.html, чтобы он мог запустить hideSpinner() где-то внутри sidebar.html.

1 Ответ

4 голосов
/ 01 октября 2019

Проблема:

Боковая панель и модальное диалоговое окно не могут обмениваться данными, несмотря на то, что они имеют одинаковое происхождение.

Решение:

Возможно получить ссылку на боковую панель HTMLот модального диалога до window.top. Оттуда можно

  • напрямую общаться друг с другом
  • использовать window.postMessage() для связи друг с другом
  • использовать куки / localalstorage для связи с каждымпрочее

Без ссылки друг на друга все еще возможно взаимодействие друг с другом через

  • службу свойств сервера и скрипта. Однако здесь один из них должен опрашивать сервер через заданные интервалы времени для получения обновлений от другого.

Пример сценария (с использованием прямого доступа):

addOn.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Addon</title>
    <style>
      #spinner {
        display: none;
        background-color: tomato;
        position: absolute;
        top: 1%;
        width: 100%;
        justify-items: center;
      }
    </style>
  </head>
  <body>
    <div id="spinner"><p>Loading modal dialog...</p></div>
    <div id="output"></div>
    <script charset="utf-8">
      google.script.run.withSuccessHandler(spinner).testModal();
      function spinner(e) {
        document.getElementById('spinner').style.display = e || 'flex';
      }
      (async () => {
        //After modal dialog has finished, receiver will be resolved
        let receiver = new Promise((res, rej) => {
          window.modalDone = res;
        });
        var message = await receiver;
        document.querySelector('#output').innerHTML = message;
        //Do what you want here
      })();
    </script>
  </body>
</html>

modalAddOn.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title></title>
  </head>
  <body>
    Modal Dialog
    <script>
      (function findSideBar(limit) {
        let f = window.top.frames;
        for (let i = 0; i < limit; ++i) {
          try {
            if (
              f[i] /*/iframedAppPanel*/ &&
              f[i].length &&
              f[i][0] && //#sandboxFrame
              f[i][0][0] && //#userHtmlFrame
              window !== f[i][0][0] //!== self
            ) {
              console.info('Sidebar found ');
              alert('Removing loadbar and closing self');
              var sidebar = f[i][0][0];
              sidebar.spinner('none'); //Remove sidebar spinner
              sidebar.modalDone('Modal says Hi'); //Modal has finished
              google.script.host.close();
            }
          } catch (e) {
            console.error(e);
            continue;
          }
        }
      })(10);
    </script>
  </body>
</html>

code.gs

function testModal() {
  SpreadsheetApp.getUi().showModelessDialog(
    HtmlService.createHtmlOutputFromFile('modalAddOn')
      .setHeight(500)
      .setWidth(300),
    ' '
  );
}

function onOpen(e) {
  SpreadsheetApp.getUi()
    .createMenu('Sidebar')
    .addItem('Show Add-On', 'showSidebar')
    .addToUi();
}

function showSidebar() {
  SpreadsheetApp.getUi().showSidebar(
    HtmlService.createTemplateFromFile('addOn.html').evaluate()
  );
}

Читать:

Смежные вопросы:

...