Виджет
Пререквизиты
Для разработки виджетов и использования их совместно с QWidget требуется версия пакета @diasoft/qpalette-visual не ниже 3.3.6.
Разработка виджетов
Виджеты разрабатываются аналогично подходу, описанному в статье Лукап в разделе "Разработка виджета для лукапа".
В данной статье предполагается, что виджет представлен модулемTaskProcessingWidgetModule
и компонентомTaskProcessingWidgetComponent
.
Для того, чтобы разрабатываемый модуль стал виджетом, необходимо его унаследовать от QWidgetModuleAbstract из @diasoft/qpalette-visual и определить геттер widgetConfig, возвращающий структуру QWidgetConfig с информацией о виджете, как в примере ниже:
import {QWidgetConfig, QWidgetManager, QWidgetModule, QWidgetModuleAbstract} from '@diasoft/qpalette-visual';
@NgModule({
declarations: [SomeWidgetComponent],
exports: [SomeWidgetComponent],
imports: [
// ...
QWidgetModule
],
bootstrap: []
})
export class SomeWidgetModule extends QWidgetModuleAbstract {
get widgetConfig(): QWidgetConfig {
return {
component: SomeWidgetComponent,
elementName: 'some-selector'
};
}
constructor(
protected injector: Injector,
protected widgetManager: QWidgetManager
) {
super(injector, widgetManager);
}
}
Необходимо:
- В component указать компонент виджета.
- В elementName указать имя компонента (селектор).
Все остальные шаги по сборке и деплою - такие же, как и в статье Лукап.
Но стоит обратить внимание, что существуют различия меджу виджетами:
<q-widget>
- подключает обычные виджеты, а именно любые куски кода, обернутые в веб-компонент, у которых есть инпуты и аутпуты.<q-lookup>
- подключает виджеты лукапа, т.е. функционал поиска, обернутый в веб-компонент, со строго определенным программным интерфейсом.
Подключение виджета в целевом продукте
Для начала, убедитесь в том, что конфигурация приложения содержит
{
// ...
"widgets": {
"bundleUrl": "/api/$service/$component/widgets/$widget/main.js"
}
}
В AppModule вашего проекта импортируется
import {QWidgetModule} from '@diasoft/qpalette-visual';
import {QCoreService} from '@diasoft/qpalette-core';
@NgModule({
imports: [
// ...
QWidgetModule.forRoot(QCoreService.config.widgets)
]
})
export class AppModule {
// ...
}
Для подключения виджета, необходимо импортировать модуль QWidgetModule из @diasoft/qpalette-visual в модуль, в котором он будет вызываться:
import {QWidgetModule} from '@diasoft/qpalette-visual';
@NgModule({
imports: [
// ...
QWidgetModule
]
})
export class SomeModule {
}
Затем использовать q-widget для загрузки разработанного виджета, как в примере ниже:
Параметры q-widget:
- source - адрес виджета в виде строки формата service:component:widget (или относительный путь до бандла виджета в случае с библиотечным UI, см. следующий раздел). Будет преобразован в URL-адрес:
/<service>/<component>/widgets/<widget>/main.js
- inputs - набор input-параметров виджета (ключ-значение). Реализует интерфейс QWidgetInputs.
- outputs - обработчики событий виджета. Реализует интерфейс QWidgetOutputs.
- widgetLoaded - событие, срабатывающее при загрузке виджета (не возвращает никаких данных).
Ниже приведён пример кода компонента, шаблон которого приведён ранее:
import {QWidgetInputs, QWidgetOutputs} from '@diasoft/qpalette-visual';
@Component({
// ...
})
export class SomeComponent implements OnInit {
source = 'service:component:widget';
widgetInputs: QWidgetInputs = {
actions: ['action-one', 'action-two'],
cancelAllowed: false
};
widgetEventHandlers: QWidgetOutputs = {
taskActionTriggered: this.onWidgetActionSelected.bind(this),
cancel: this.onWidgetCancel.bind(this),
};
onWidgetActionSelected(event: any): void {
console.log(event.detail);
}
onWidgetCancel(event: any): void {
console.log(event.detail);
}
widgetLoaded(event: any): void {
//do something
}
}
Обратите внимание, что если в событии компонента передаются какие-то данные, то они будут содержаться в полеdetail
события.
Ниже приведён пример кода самого виджета, который принимает на вход массивactions
и отдаёт событияtaskActionTriggered
иcanceled
:
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
@Component({
...
})
export class TaskWidgetComponent implements OnInit {
private cancellable = false;
@Input() set actions(actions: string[]) {
console.log('Доступные действия: ', actions);
}
@Input() set cancelAllowed(allowed: boolean) {
console.log(`Отмена ${allowed ? 'доступна' : 'недоступна'}`);
this.cancellable = allowed;
}
@Output() actionTriggered = new EventEmitter<string>();
@Output() canceled = new EventEmitter<void>();
ngOnInit(): void {
this.actionTriggered.emit('action-one');
if (this.cancellable) {
this.canceled.emit();
}
}
}
Подключение виджета из библиотечного проекта
Обновитесь до Q.Palette 5.3.15 и выше для работы этого функционала.
Если вы подключили в свой проект библиотечный UI, который содержит виджеты, вы можете их также подключить, проделав следующие шаги.
Введите название вашего UI-сервиса, компонента, подключаемого библиотечного UI и виджета - и мы подставим их в инструкции ниже:
Имя сервиса прикладного UI * Имя компонента прикладного UI * Имя пакета библиотечного UI * Имя подключаемого виджета *
В файле angular.json
найдите секцию projects.[project].architect.build.options.assets
, где [project]
- это название вашего проекта, в который вы хотите подключить лукап.
Добавьте в данную секцию следующий блок кода, где libraryWidgetForm.installedLibraryPackage
- название подключенного библиотечного UI.
Теперь после каждой сборки проекта в папке assets/libraryWidgetForm.installedLibraryPackage
будет находиться бандл самого библиотечного UI, а также все бандлы виджетов (как правило, находятся в папке assets/libraryWidgetForm.installedLibraryPackage/widgets
).
Подключите лукап с указанием полного относительного пути к виджету в параметре source
:
Обратите внимание, что адрес начинается с пути /api/libraryWidgetForm.service/libraryWidgetForm.component
, который является путем к веб-компоненту, в который вы подключаете библиотечный UI. Подставьте вместо него адрес своего веб-компонента. Также обратите внимание на папку /widgets/libraryWidgetForm.widget
: виджет может оказаться в другой папке по вине разработчика виджета - в этом случае поищите виджет вручную в папке /assets/libraryWidgetForm.installedLibraryPackage
вашего компонента и подставьте правильный путь.
Ниже пример лукапа, подключающего виджет из библиотечного UI нашей документации:
APP_BASE_HREF
Зачем виджету нужен APP_BASE_HREF?
APP_BASE_HREF
нужен в любых приложениях, у которых есть базовый url-адрес.
Пример
- Мы открываем страницу приложения
/customer-search-widget
, на которой расположен виджет. - При загрузке виджет пытается сопоставить текущий адрес
/customer-search-widget
с теми, которые указаны в настройках роутера этого виджета. - Виджет не находит такого адреса, пишет в консоль
Error: Cannot match any routes. URL Segment: 'customer-search-widget'
и редиректит в корень ("/").
Установка
Чтобы этого избежать, мы должны установить адрес/customer-search-widget
в качестве базового в приложении виджета. Это можно сделать установкой провайдераAPP_BASE_HREF
:
{provide: APP_BASE_HREF, useValue: window.location.pathname}
В момент подгрузки виджета переменнаяwindow.location.pathname
будет равна/customer-search-widget
, что и станет базовым адресом виджета.
После установки
Теперь поведение, начиная с п. 2 меняется:
-
Виджет при загрузке ищет в настройках роутера уже не адрес
/customer-search-widget
, а ту часть, которая идёт после него. Поскольку в нашем случае эта часть пуста, то виджет ищет в настройках роутера пустой адрес ('') - и находит его: -
Виджет выводит компонент
SearchIdDocComponent
.Точно такое же поведение при навигации по другим маршрутам относительно
/customer-search-widget
:/customer-search-widget/search-full-name
/customer-search-widget/customer-main-info
Во всех этих случаях виджет будет теперь искать именно маршруты/search-full-name
и/customer-main-info
, так как часть/customer-search-widget
установлена в качестве базового URL.
См. также
Подробнее можно почитать в статьях:
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base (opens in a new tab)
- https://angular.io/guide/router#html5-urls-and-the--base-href (opens in a new tab)