Skip to content

Жизненный цикл CustomWidget ​

Контракт AMD-модуля ​

script.js обязан вызвать define() синхронно, во время своего исполнения, а фабрика должна вернуть функцию-конструктор. Конструктор вызывается через new, и у полученного экземпляра ожидается объект callbacks.

js
define(['jquery'], function ($) {
  return function CustomWidget() {
    this.callbacks = {
      render:       function (container) { return true },
      init:         function () { return true },
      bind_actions: function () { return true },
      initMenuPage: function (params) { return true },
      settings:     function ($modal) { return true },
      onSave:       function (settings) { return true },
      destroy:      function () { return true },
    }
  }
})

Загрузчик намеренно минимален — это не полноценная реализация AMD:

  • Разрешаются только имена зависимостей 'jquery' и '$', обе — в jQuery 4, который поставляет Bito. Любое другое имя разрешается в undefined — ни загрузки с CDN, ни реестра модулей.
  • Нет именованных модулей, нет вложенного require(), нет кеша модулей.
  • Если скрипт загрузился, но define() так и не вызвал, или define() вернул не функцию, монтирование завершается карточкой ошибки.
  • Скрипты виджетов загружаются по одному. Единственная прослойка define обслуживает одну загрузку за раз, поэтому два виджета на одном экране загрузятся последовательно, а не параллельно.

Ничто из этого не является песочницей. Скрипт исполняется в origin админ-панели; см. обзор.

Колбэки ​

Все колбэки необязательны. Отсутствующий колбэк просто пропускается — это не ошибка.

КолбэкЧто получаетКогда вызывается
renderDOM-элемент, в который виджет должен отрисоватьсяПервым, при монтировании
init—После render
bind_actions—После init
initMenuPage{ location, item_code, subitem_code }Только на страницах левого меню: после bind_actions, затем при каждом переключении подпункта — см. ниже
settingsТело модального окна настроек, обёрнутое прослойкой $Когда пользователь открывает настройки виджета
onSaveСобранный объект настроекКогда пользователь нажимает «Сохранить» в этом окне
destroy—При размонтировании, всегда

render → init → bind_actions вызываются именно в этом порядке при каждом монтировании и прерываются, если виджет по дороге отключат.

INFO

Как и в amoCRM, Bito передаёт в settings обёрнутое тело окна: $modal — это объект-коллекция, а не сырой DOM-узел, и это тот же $, в который разрешается зависимость 'jquery' вашего модуля. Поэтому $modal.find('input[name=api_token]').val() работает как написано — см. Совместимость с jQuery.

Что приходит в onSave ​

Bito ищет первый <form> внутри тела окна настроек и отдаёт вам его FormData в виде обычного объекта. Если в вашей разметке настроек нет <form>, вы получите {} — делайте форму настоящим <form> с именованными полями.

js
onSave: function (settings) {
  if (!settings.api_token) {
    bito.notify({ message: bito.i18n('token_required'), severity: 'error' })
    return false          // блокирует сохранение
  }
  return bito.settings.save(settings).then(function () { return true })
}

initMenuPage ​

Вызывается только на страницах левого меню — в точке widget_page. Получает открытую страницу — тот же объект, что и bito.context.menu:

js
initMenuPage: function (params) {
  // { location: "widget_page", item_code: "acme_crm", subitem_code: null }
  // { location: "saleMain", item_code: "acme_crm", subitem_code: "order_sync" }
  root.innerHTML = renderPage(params.subitem_code)   // root: контейнер, полученный render()
}
  • Первый раз вызывается один раз, сразу после bind_actions.
  • Когда пользователь открывает другой подпункт того же блока (того же location), Bito не перемонтирует виджет: контейнер и состояние сохраняются, bito.context.menu обновляется, и initMenuPage вызывается снова с новым объектом. Новую страницу отрисовываете вы.
  • Если в виджете нет initMenuPage, Bito вместо этого перемонтирует его при каждом переключении подпункта — destroy, затем render → init → bind_actions, — так что простой виджет, читающий bito.context.menu в render, всё равно работает.
  • Переход в другой блок или уход со страниц виджета размонтирует его и вызывает destroy.
  • Исключение или отклонённый промис перехватывается, логируется и засчитывается в тот же бюджет исключений (два), что и у остальных колбэков. Возвращаемое значение игнорируется, карточка ошибки для него не показывается.

Сдерживание сбоев ​

Каждый вызов колбэка обёрнут. Сбой вашего виджета не должен «обелять» экран ERP, поэтому:

  1. Колбэк, бросивший исключение, перехватывается и логируется, а вместо виджета рисуется карточка ошибки. Виджет продолжает работать.
  2. Второе исключение — любого колбэка, в любой момент — отключает виджет до конца этого монтирования. Дальше ничего не вызывается.
  3. Флаг живёт на смонтированном экземпляре. Уход со страницы и возврат монтируют виджет заново и обнуляют счётчик; он не сохраняется.

Отклонённый промис считается точно так же, как синхронное исключение: колбэки ожидаются через await.

onSave блокирует сохранение ​

onSave — единственный колбэк, чьё возвращаемое значение меняет поведение хоста.

Ваш onSaveРезультат
Вернул falseСохранение заблокировано с понятным сообщением. В бюджет исключений это не засчитывается — возврат false это работающий документированный API, а не сбой.
Бросил исключение или отклонил промисСохранение заблокировано, и это считается исключением.
Выполнялся дольше 30 секундСохранение заблокировано с сообщением о таймауте, и это считается исключением.
Вернул что-то иное или ничегоСохранение проходит.

Если к моменту нажатия «Сохранить» виджет уже отключён, сохранение блокируется сразу.

destroy выполняется всегда ​

destroy вызывается при размонтировании даже после того, как более ранний колбэк бросил исключение, и даже когда виджет уже отключён — он намеренно не зависит от флага отключения. Его собственные исключения проглатываются и логируются: уборка не должна ломаться сама о себя.

Используйте его, чтобы снять таймеры, отменить незавершённые запросы и отсоединить всё, что вы прицепили вне собственного контейнера. Больше за вами никто не убирает: ваш <link> со стилями и ваш тег <script> удаляются, но глобальные слушатели, интервалы и DOM, который вы вставили в другом месте, — ваша забота.

Bito 2.0 Integration API