@artsofte/esw
v0.0.6
Published
Esw types
Downloads
11
Readme
ESW Docs
Общее
Платформа nocode имеет возможность встраивания виджетов. Виджет - это программный код выполняющийся на стороне нашей платформы. Виджет может отображать данные, делать запросы на сторонние ресурсы, взаимодействовать с сущностями платформы nocode
Реализация виджета
Простейший виджет
Сначала вам нужно реализовать сам виджет. Виджет - это js файл. Так выглядит самый простой виджет, который выводит на экран текст "Hello, world"
window.eswInit = () => {
return new SimpleWidget();
};
class SimpleWidget {
constructor() {
const renderRoot: HTMLElement = getEswNamespace().renderRoot;
renderRoot.innerText = "Hello, NoCode!";
}
}
Наша платформа при работе с вашим файлом рассчитывает на наличие глобальной функции eswInit, которая возвращает инстанс виджета. Далее у этого инстанса будут вызываться хуки жизненного цикла.
Использование TypeScript
Мы предоставляем npm пакет с декларациями типов сущностей платформы. Загрузить его можно с помощью команды
npm i @artsofte/esw
Исходный код и примеры
С кодом библиотеки @artsofte/esw
можно ознакомиться на Github. Также можете ознакомиться с примерами виджетов Examples
Хуки жизненного цикла виджета
EswIsValid
Пример использования:
import { EswIsValid } from "@artsofte/esw";
window.eswInit = () => {
return new SimpleWidget();
};
class SimpleWidget implements EswIsValid {
public eswIsValid(): boolean {
return true;
}
}
При попытке клиента перехода на следующий шаг - виджеты на форме должны быть валидными. Если виджет не валиден, то система не позволяет пройти на следующий шаг. Если ваш виджет реализует интерфейс EswIsValid
, то при попытке перехода на следующий шаг будет вызван метод eswIsValid
.
EswAfterUnmount
Пример использования:
import { EswAfterUnmount } from "@artsofte/esw";
window.eswInit = () => {
return new SimpleWidget();
};
class SimpleWidget implements EswAfterUnmount {
private intervalId: number;
constructor() {
this.intervalId = setInterval(() => console.log("Hello, world"), 1000);
}
public eswAfterUnmount(): void {
if (this.intervalId) {
clearInterval(this.intervalId);
}
}
}
Хук, обозначающий, что разметка вашего виджета уже удалена из DOM. Чаще всего используется для очистки таймеров, подписок.
EswOnSettingsChange
Пример использования:
import { Catalog, EswOnSettingsChange, GetSettingsReturn } from "@artsofte/esw";
interface IWidgetSettings {
catalog: Catalog<{ name: string; value: string }>;
}
window.eswInit = (config: IWidgetSettings) => {
return new CatalogReader(config);
};
window.eswInitSettings = (): GetSettingsReturn => {
return {
version: "v1",
settings: [
{
type: "catalog",
label: "Отображаемый каталог",
saveTo: "catalog",
},
],
};
};
class CatalogReader implements EswOnSettingsChange {
private readonly renderRoot: HTMLElement = getEswNamespace().renderRoot;
constructor(private config: IWidgetSettings) {}
public eswOnSettingsChange(config: IWidgetSettings): void {
this.config = config;
this.renderCatalogData();
}
private renderCatalogData(): void {
this.config.catalog.content().then((data) => {
this.renderRoot.innerText = JSON.stringify(data);
});
}
}
Данный хук вызывается при каждом изменении настроек в правом сайд-баре со стороны провайдера. Стоит обратить внимание на то, что данный хук не будет ни разу вызван при рендеринге виджета на клиентской форме.
Вывод настроек для провайдера
Платформа имеет встроенные механизмы справочников, модели документа и другие. Виджету требуется взаимодействие с ними. Всегда доступ к тем или иным сущностям выдает провайдер в правом сайд-баре.
Например, виджет хочет записывать данные в модель документа. Тогда провайдер в правом сайде-баре выбирает область в модели документа, куда виджет имеет право писать данные. Другой пример, виджет хочет получить доступ до каталога: провайдер выбирает к какому каталогу именно виджет имеет доступ.
Чтобы указать, какие настройки требуются - реализуйте глобальную функцию eswInitSettings
. У каждой настройки есть параметр saveTo, этот параметр обозначает путь, куда в конфиге буду сохранены данные.
window.eswInitSettings = (): EswSettingsDeclaration => {
return {
version: "v1",
settings: [
{
...,
saveTo: "param"
}
]
}
}
interface WidgetSettings {
param: ...
}
window.eswInit = (config: WidgetSettings) => {
...
}
Важно понимать, что при рендеринге на стороне провайдера config в метод eswInit будет пустым, так как провайдер еще не выбрал настройки. Объект будет наполняться по мере наполнения настроек провайдером.
Запись в модель документа
Виджет может записывать данные в модель документа. Для этого виджет должен вернуть требуемые настройки в методе initSettings
import {
DocumentElement,
EswAfterUnmount,
GetSettingsReturn,
} from "@artsofte/esw";
window.eswInit = (config: IWidgetSettings) => {
return new SimpleWidget(config);
};
interface IWidgetSettings {
documentToken: DocumentElement<{ someData: string }>;
}
window.eswInitSettings = (): EswSettingsDeclaration => {
return {
version: "v1",
settings: [
{
type: "documentElementWrite",
label: "Запись в модель документа",
saveTo: "documentToken",
isRequired: true,
},
],
};
};
class SimpleWidget implements EswAfterUnmount {
private readonly renderRoot: HTMLElement = getEswNamespace().renderRoot;
private readonly intrevalId: number = 0;
constructor(config: IWidgetSettings) {
const isProvider = getEswNamespace().env.isProvider;
if (isProvider) {
this.renderProviderView();
return;
}
let count = 0;
this.intrevalId = setInterval(() => {
count += 1;
config.documentToken.patch("count", { value: count });
this.renderRoot.innerHTML = `Count: ${count}`;
}, 1000);
}
public eswAfterUnmount() {
clearInterval(this.intrevalId);
}
private renderProviderView() {
this.renderRoot.innerHTML = "Виджет патчит документ каждую секунду";
}
}
Справочники
Виджет может читать данные из справочника, который к нему привяжет провайдер
import { Catalog, GetSettingsReturn } from "@artsfote/esw";
interface IWidgetSettings {
catalog: Catalog<{ name: string; value: string }>;
}
window.eswInit = (config: IWidgetSettings) => {
return new CatalogReader(config);
};
window.eswInitSettings = (): EswSettingsDeclaration => {
return {
version: "v1",
settings: [
{
type: "catalog",
label: "Отображаемый каталог",
saveTo: "catalog",
},
],
};
};
class CatalogReader {
private readonly renderRoot: HTMLElement = getEswNamespace().renderRoot;
constructor(config: IWidgetSettings) {
const isProvider = getEswNamespace().env.isProvider;
if (isProvider) {
this.renderProviderView();
} else {
config.catalog.content()
.then((data) => this.render(data))
.catch(() => this.renderError());
}
}
private renderProviderView(): void {
this.renderRoot.innerHTML = "Отображение каталогов";
}
private render(data: any): void {
this.renderRoot.innerHTML = JSON.stringify(data);
}
private renderError(): void {
this.renderRoot.innerHTML = "Ошибка чтения справочника";
}
}
Другие настройки
select
отображение селекта. ВsaveTo
будет записанSelectItem
Использование бибилотек и фреймворков
Разработчик виджета может использовать любые доступные технологии. В директории examples есть примеры использования некоторых технологий.
Платформа разрешает загружать как единый JS бандл, так и дает возможность загрузки множества скриптов по url.
Нужно учесть, что рендеринг вашего скрипта происходит в shadow root, поэтому, нужно указать вашей библиотеке/фреймворку в какой именно HTML узел помещать разметку. Сам этот HTML узел можно получить вызвав следующий код: getEswNamespace().renderRoot
Ограничения
Рендеринг вашего html происходит вне встроенного iframe, а в рамках основного DOM в shadow-root. Это значит, что существует две разные среды JS, а значит разные инстансы глобальных объектов. Например, вы прослушиваете событие клика по кнопке. К прослушиванию добавляете callback, который принимает event с типом MouseEvent
. Событие происходит в рамках основного DOM и передается в функцию, которая работает в рамках iframe. Поэтому проверка event instanceof MouseEvent
не сработает. Так как event был создан в рамках основного окна и в прототипе лежит ссылка на MouseEvent основного окна. А вы совершаете проверку с MouseEvent вашего окна, в котором свои глобальные объект, никак не связанные с глобальными объектами основного окна.
Также часто можно наблюдать, когда создается прослушиватель на весь документ (document.addEventListener('click', () => {})
). Это не будет работать в рамках виджета, так как document
будет являться документом айфрейма. А так как iframe находится в head секции основного DOM, то клик по нему никогда не происходит. Нужно создавать прослушиватели только на свой корневой div элемент. Получить его можно при помощи getEswNamespace().renderRoot