Обработка ошибок в платформенных контролах

Контролы со встроенной обработкой ошибок

В контролы Wasaby Framework, работающие с источниками данных ISource, внедрён механизм обработки ошибок:

  1. Controls/list:View;
  2. Controls/grid:View;
  3. Controls/treeGrid:View;
  4. Controls/tile:View;
  5. Controls/explorer:View;
  6. Controls/form:Controller;
  7. Controls/dataSource:error.DataLoader.

Если прикладному разработчику не требуется особая логика обработки ошибок, то нет необходимости писать собственный код. Контрол самостоятельно обработает ошибку без какой-либо дополнительной настройки.

Для случаев, когда особая логика всё же требуется, у этих контролов есть опция errorController, через которую можно внедрять в них свою обработку ошибок.

Как повлиять на обработку и отображение ошибок в платформенном контроле

Функция onProcess

Функция onProcess предназначена для изменения ErrorViewConfig. Она принимает ErrorViewConfig после его окончательного формирования и должна вернуть ErrorViewConfig, который и будет использован для отображения дружелюбной ошибки. В функции можно изменить ErrorViewConfig под свои нужды – изменить шаблон ошибки, опции шаблона, текст и комментарий ошибки и т.д.

Используя функцию onProcess, можно не прибегать к созданию собственного обработчика ошибки, если не требуется обработать специфичную ошибку. Если требуется ориентироваться на тип стандартной ошибки, то это можно сделать, обратившись к соответствующему полю ErrorViewConfig.

Использовать функцию onProcess можно тремя способами:

  1. Если вы показываете ошибку только в диалоговом окне

Функция Controls/error:process кроме ошибки может принять в параметрах функцию onProcess.

  1. Если вы сами создаете экземпляр ErrorController

ErrorController принимает в конструктор объект, в котором присутствует опциональная функция onProcess. Также вы можете пользоваться методами экземпляра setOnProcess и updateOnProcess.

  1. Если вы используете чужой экземпляр ErrorController

Пользуйтесь методами экземпляра setOnProcess и updateOnProcess.

Тип стандартной ошибки в конфигурации отображения

Если ошибка была обработана одним из стандартных обработчиков, то в ErrorViewConfig запишется поле type: ErrorType. На это поле можно ориентироваться, если нужно поменять данные конфигурации отображения только для некоторых типовых ошибок.

Повторение действия, приведшего к показу дружелюбной ошибки

Иногда кнопка-действие, использующаяся в шаблонах типовых ошибок не подходит или мы хотим дать пользователю альтернативу в действиях. Например, в шаблонах для ошибок 502 - 504 используется кнопка-действие, перезагружающая страницу. Такое поведение подходит не всем. Для того, чтобы изменить это поведение, можно создать свой класс действия, наследующийся от BaseAction или LinkAction (если это просто ссылка), и настроить в нем свое поведение при нажатии на кнопку. Но если требуется, например, просто повторить запрос к БЛ с нужными параметрами, то лучше воспользоваться функцией onProcess и в ErrorViewConfig задать поле repeatConfig.

Поля repeatConfig:

  • caption – позволит задать текст для кнопки повтора действия, если стандартное "Попробовать еще раз" вам не подойдет;
  • display – нужно ли отображать кнопку повтора действия. По умолчанию false. Этот флаг нужен для тех случаев, когда какой-либо контрол уже предоставляет свою конфигурацию для повтора действия, например, список. С помощью этого флага вы, по желанию, сможете показать кнопку с переданной конфигурацией;
  • function – функция, которая вызовется при нажатии на кнопку. Если ваш запрос происходит с какими-либо параметрами и может повлиять на внутреннее состояние вашего контрола, то в таком случае нужно забиндить функцию запроса и передать ее в это поле.

Модуль для обработки ошибок

Для обработки ошибок используется механизм интерфейса и реализаций.

При обработке ошибок этот механизм реализуется модулями:

  • ErrorHandling – модуль интерфейса для обработки ошибок.
  • ErrorHandlingSimple – модуль реализации для обработки ошибок. Содержит один обработчик, выводящий текст ошибки.
  • SbisEnvUI – модуль реализации для обработки ошибок. Содержит обработчики ошибок для СБИС.

ErrorController при обработке ошибок получает обработчики с помощью метода ErrorHandling/ErrorHandling:getHandlers(). Если в вашем приложении возникла потребность использовать какие-то специфичные обработчики, то лучше всего поступить следующим образом:

  1. Создать модуль, реализующий интерфейс ErrorHandling.
  2. Указать, что модуль предоставляет реализацию интерфейса ErrorHandling.
  3. Унаследоваться от модуля SbisEnvUI, если вам нужны обработчики СБИС.

Указанные этапы описаны ниже в примере.

Файл MyPrecious.s3mod

<!-- 1. Создаем модуль MyPrecious. Реализацию опишем в ts файле -->
<ui_module id="123" name="MyPrecious">

  <!-- Не забудем прописать зависимости -->
  <depends>
    <module id="50f80520-0983-4049-b17a-d893bce91d69" name="ErrorHandling"/>
    <module id="a10cb1cb-b23d-4ef6-a757-4f7382e0e8d1" name="SbisEnvUI"/>
  </depends>

  <!-- 2. Указываем, что модуль предоставляет реализацию интерфейса ErrorHandling -->
  <features_provided>
    <feature name="ErrorHandling"/>
  </features_provided>

  <!-- 3. Таким образом наследуемся от SbisEnvUI -->
  <load_after>
    <module id="a10cb1cb-b23d-4ef6-a757-4f7382e0e8d1" name="SbisEnvUI"/>
  </load_after>
</ui_module>

Файл MyPrecious/ErrorHandling.ts

import { ErrorHandler } from 'ErrorHandling/interface';
import { getHandlers as getSbisHandlers } from 'SbisEnvUI/ErrorHandling';
 
// Это наши специфические обработчики для приложения
const mySpecificHandlers: ErrorHandler[] = [<some_handlers>];
 
// Реализуем интерфейс ErrorHandling
export const getHandlers: () => ErrorHandler[] = () => [...mySpecificHandlers, ...getSbisHandlers()];

А это файл с нашим сервисом Precious.s3srv

<service id="42" name="Precious">
  <items>
    <ui_module id="50f80520-0983-4049-b17a-d893bce91d69" name="ErrorHandling" url="$(SBISPlatformSDK)/ui-modules/ErrorHandling/ErrorHandling.s3mod"/>
    <ui_module id="123" name="MyPrecious" ... >
  </items>
</service>

Списочные контролы

Чтобы повлиять на обработку и отображение ошибки в списочных контролах, нужно создать свой контроллер ошибок и передать его в опцию errorController контрола.

В следующем примере мы повлияем на отображение ошибки в платформенном списке:

  1. Изменим размер заглушки.
  2. Зададим свою картинку для ошибки 403.
  3. Покажем кнопку для повтора действия, если список предоставит нам такую информацию.
  4. Скроем стандартную кнопку-действие.

MyComponent.ts

import * as template from 'wml!MyComponent';
import { Control, TemplateFunction } from 'UI/Base';
import { ErrorController, ErrorType } from 'Controls/error';
import { Parking } from 'SbisEnvUI/Maintains';
 
export default class MyComponent extends Control {
    protected _template: TemplateFunction = template;
 
    /**
     * Создаём свой контроллер ошибок.
     * Указываем предпочтительные параметры отображения ошибки в функции onProcess.
     * Эти параметры будут объединены с теми, которые вернёт обработчик ошибки.
     */
    protected _errorController = new ErrorController({
        onProcess: (viewConfig) => {
            // 1. Меняем размер заглушки
            viewConfig.options.size = Parking.Const.Size.normal;

            // 2. Задаем свою картинку для ошибки 403
            if (viewConfig.type === ErrorType.accessDenied) {
                viewConfig.options.image = 'my_perfect_image_url';
            }

            // 3. Покажем кнопку для повтора действия, если список передал соответствующую конфигурацию
            if (viewConfig.options.repeatConfig) {
                viewConfig.options.repeatConfig.display = true;
            }

            // 4. Скроем стандартную кнопку-действие
            if (viewConfig.options.action) {
                delete viewConfig.options.action;
            }

            return viewConfig;
        }
    });
}
<!-- MyComponent.wml -->
<Controls.list:View errorController="{{_errorController}}" ... />

Удаление в списках

Чтобы обработать ошибку удаления в списках, используйте методы интерфейса Controls/list:IRemovableList, который по умолчанию подключен в списки. Методы возвращают промис, в .catch() которого можно задать обработчик ошибки.

FormController

Иногда стандартной обработки ошибок, возникающих в процессе работы Controls/form:Controller, недостаточно. Допустим, что форм-контроллер не используется в раскладке выезжающей панели, а сам содержит в себе эту раскладку. В таком случае при возникновении ошибки контентная область Controls/form:Controller может не построиться. Решением проблемы может стать создание раскладки, которая будет отображаться при возникновении ошибки.

Для того, чтобы повлиять на обработку и отображение ошибок в Controls/form:Controller, нужно воспользоваться следующими его опциями:

  • errorController - позволит обработать ошибку;
  • errorContainer - позволит создать собственный шаблон для отображения ошибки.

Рассмотрим пример, в котором Controls/form:Controller используется внутри раскладки Controls.popup:Stack. При возникновении ошибки отобразится стандартный Controls/dataSource:error.Container без учета раскладки, то есть, например, у него не будет кнопки закрытия панели Controls.popup:Stack.

<!-- MyModule/StackTemplate.wml -->
<Controls.form:Controller
   ...
   errorContainer="{{ _errorContainer }}"
   record="{{ _record }}"
>
   <Controls.popupTemplate:Stack>
      <!-- Некое содержимое -->
   </Controls.popupTemplate:Stack>
</Controls.form:Controller>
...
// MyModule/StackTemplate.ts
import { Control, TemplateFunction } from 'UI/Base';
import * as template from 'wml!MyModule/Dialog';
// импортируем для использования интерфейса ErrorContainer'а - Controls/dataSource:error.IContainerConstructor
import { error as dataSourceError } from 'Controls/dataSource';
// импортируем наш ErrorContainer
import { default as errorContainer } from 'MyModule/ErrorContainer';
// ...
 
export default class StackTemplate extends Control {
   protected _template: TemplateFunction = template;
   protected _errorContainer: dataSourceError.IContainerConstructor = errorContainer;
 
   // ...
}
<!-- MyModule/ErrorContainer.wml -->
<ws:if data="{{ _options.viewConfig }}">
   <Controls.popupTemplate:Stack>
      <ws:bodyContentTemplate>
         <Controls.dataSource:error.Container
            name="errorContainer"
            viewConfig="{{ _options.viewConfig }}"
         />
      </ws:bodyContentTemplate>
   </Controls.popupTemplate:Stack>
</ws:if>
<ws:else>
   <ws:partial template="{{ _options.content }}"/>
</ws:else>
// MyModule/ErrorContainer.ts
import { Control, TemplateFunction } from 'UI/Base';
import * as template from 'wml!MyModule/ErrorContainer';
 
export default class extends Control {
   protected _template: TemplateFunction = template;
 
   show(viewConfig: unknown): void {
       this._children.errorContainer.show(viewConfig);
   }
 
   hide(): void {
       this._children.errorContainer.hide();
   }
 
   // ...
}

Отображение одной ошибки для нескольких списочных контролов

Стоит учитывать, что если на вашей странице присутствует несколько списочных контролов, то не должно выводиться несколько одинаковых ошибок. Чаще всего подобная ситуация возникает при использовании контрола Controls/masterDetail:Base, который содержит в себе 2 списка. В подобных случаях необходимо использовать Controls.dataSource:error.DataLoader, который обернет несколько списков и отобразит одну ошибку.

Пример использования Controls.dataSource:error.DataLoader

<!-- WML -->
<div class="MyComponent">
   <Controls.dataSource:error.DataLoader
      sources="{{_sources}}"
      errorController="{{_errorController}}">
      <Controls.masterDetail:Base>
         <ws:master>
            <Controls.grid:View
               source="{{content.sources[0].source}}"
               keyProperty="id"
               parentProperty="parent"/>
         </ws:master>
         <ws:detail>
            <Controls.list:View
               keyProperty="id"
               source="{{content.sources[1].source}}"/>
         </ws:detail>
      </Controls.masterDetail:Base>
   </Controls.dataSource:error.DataLoader>
</div>
// TypeScript
// Это обработчик, в котором мы распознаём специфичные ошибки от нашей БЛ.
import { myHandler } from './handler';
 
import * as template from 'wml!myModule';
import { SbisService } from 'Types/source';
import { Control, TemplateFunction } from 'UI/Base';
import { error as dataSourceError, requestDataUtil } from 'Controls/dataSource';
 
export default class MyComponent extends Control {
    protected _template: TemplateFunction = template;
 
    /**
     * Создаём конфигурацию наших источников данных.
     */
    protected _sources: requestDataUtil.ISourceConfig[] = [{
        source: new SbisService(/* ... */)
    }, {
        source: new SbisService(/* ... */),
        filter: {/* ... */},
        historyId: 'myHistoryId'
    }];
 
    /**
     * Создаём свой контроллер ошибок (экземпляр Controls/error:ErrorController).
     * Добавляем в него свой обработчик (myHandler).
     */
    protected _errorController = new dataSourceError.Controller({
        handlers: [myHandler]
    });
}

Обработчики типовых ошибок

Для обработки ошибок в рамках приложения СБИС используются обработчики типовых ошибок, внедрённых на уровне приложения:

  1. Ошибка загрузки ресурсов через RequireJS — SbisEnvUI/Maintains:Parking.handlers.require.
  2. Внутренняя ошибка сервиса (код состояния HTTP 500) — SbisEnvUI/Maintains:Parking.handlers.internal.
  3. Ресурс не найден (код состояния HTTP 404) — SbisEnvUI/Maintains:Parking.handlers.notFound.
  4. Сервис на тех.обслуживании (коды состояния HTTP 502, 503, 504) — SbisEnvUI/Maintains:Parking.handlers.maintenance.
  5. Ошибка сети — SbisEnvUI/Maintains:Parking.handlers.connection.
  6. Ошибка доступа (код состояния HTTP 403) — SbisEnvUI/Maintains:Parking.handlers.accessDenied.
  7. Ошибка БЛ — SbisEnvUI/Maintains:Parking.handlers.rpc.

Пример расширения платформенного обработчика ошибки:

import { Parking } from 'SbisEnvUI/Maintains'; 
/**
 * Создадим свой обработчик ошибки на основе платформенного.
 * Нам нужно поменять текст сообщения.
 */
function myInternalHandler(config) {
   const result = Parking.handlers.internal(config);
   if (!result) {
      return;
   }
   return {
      ...result,
      options: {
         ...result.options,
         message: 'MyMessage'
      }
   };
}

Базовые шаблоны ошибок

SbisEnvUI/Maintains:Parking.templates.*

  1. Body — основной:
    • изображения,
    • основной текст ошибки,
    • дополнительный текст,
    • предлагаемое пользователю действие.
  2. Include — шаблон, заменяющий содержимое контрола.
  3. Dialog — диалоговое окно.
  4. Page — шаблон отображения парковочной на всю страницу.
  5. UpdateNews — страница обновления сервиса.
  6. getBaseTemplateForMode(mode: Mode): Include|Dialog|Page — функция для получения шаблона по режиму отображения.

Контролы предлагаемых пользователю действий (кнопки-ссылки):

SbisEnvUI/Maintains:Parking.actions.*

  1. Reload — перезагрузить страницу.
  2. MainPage — перейти на главную.
  3. AskAdmin — обратиться к администратору системы.

Наборы констант:

SbisEnvUI/Maintains:Parking.Const.*

  1. MESSAGE — сообщения об ошибках.
  2. DETAILS — дополнительный текст: как поступить пользователю, если возникла ошибка.
  3. IMAGE — изображения к ошибкам.
  4. ACTION — текст для предлагаемых пользователю действий.
Пример
Исходный код