Механизм внедрения зависимостей на клиенте
В данном разделе рассмотрены особенности передачи клиентских сервисов с использованием механизма внедрения зависимостей.
В Web-клиенте внедрение зависимостей связано с таким понятием, как контейнер сервисов, упрощенная реализация которого приведена ниже.
var serviceContainer = {
urlStore: new UrlStroe(),
requestManager: new ReqeustManager(),
router: new Router()
}
Данный контейнер содержит три сервиса, которые могут быть переданы в метод при его вызове:
sendToAcquaintance(serviceContainer);
Полноценная реализация такого контейнера в Web-клиенте определена в классе ServiceContainer
, экземпляр которого доступен из статического свойства WebClient.App.Instance
и глобальной переменной WebClient.app
.
Данный контейнер содержит все стандартные сервисы Web-клиента (приведены в JSDoc API).
Отличительной особенностью реализации контейнера ServiceContainer
является то, что каждое свойство в нем имеет get-функцию, в которой динамически вычисляется искомое значение. В частности, для каждого сервиса в данном контейнере создаётся объект ServiceDescriptor
, содержащий информацию о том, как должно вычисляться его значение.
Класс ServiceContainer
предоставляет функции для создания и регистрация дескрипторов, которые в WebClient.App.Instance
представлены в виде статически методов, пример использования которых приведён ниже.
WebClient.App.registerService("urlStore", new UrlStore()); (1)
WebClient.App.addService<$UrlStore>({ urlStore: new UrlStore()}); (2)
WebClient.App.registerServiceFactory("urlStore", (services) => new UrlStore()); (3)
WebClient.App.addServiceFactory<$UrlStore>({ urlStore: (services) => new UrlStore()}); (4)
WebClient.App.registerServiceAccessors("now", () => new Date()); (5)
let serviceDescriptor = { (6)
name: "urlStore",
instance: new UrlStore()
};
WebClient.App.registerServiceDesciptor(serviceDescriptor);
var myUrlStore = WebClient.app.with<$UrlStore>().urlStore; (7)
1 | Регистрация с передачей имени свойства и значения. |
2 | Вариант регистрации с передачей объекта. |
3 | Регистрация фабрики сервиса с передачей имени. Фабрика будет вызвана при первом обращении к сервису, после чего значение будет кэширована.
Функция принимает объект контейнера, актуального на момент её вызова. |
4 | Вариант регистрации фабрики сервиса с передачей объекта. |
5 | Регистрация динамически вычисляемого значения.
Функция будет вызываться при каждом обращении к сервису. Можно также передать set-функцию. |
6 | Передача дескриптора сервиса созданного вручную. |
7 | Использование зарегистрированного сервиса. |
Сервисы в разметке
Некоторые сервисы, такие как RequestManager
, должны быть созданы для каждой разметки заново. Такие сервисы регистрируются специальным образом, с передачей дополнительных метаданных:
WebClient.App.addServiceFactory<$RequestManager>( (1)
{ requestManager: (services: $Layout) => new RequestManager(services) },
WebClient.LAYOUT_SERVICE
);
1 | Сервис специфичный для разметки. Передаем дополнительным параметром метаданные — объект WebClient.LAYOUT_SERVICE |
При открытии разметки, будет создана копия контейнера WebClient.App.Instance
, в этой копии все сервисы зарегистрированные как фабрики с передачей WebClient.LAYOUT_SERVICE
будут созданы повторно. То есть, фабричная функция будет вызвана для получения нового экземпляра, специфичного для данной разметки.
Для доступа к контейнеру сервисов разметки можно либо напрямую обратиться к параметру services
в элементе Layout, либо объявить параметр services
в своем элементе управления (см. пункт Получение сервисов в клиентском компоненте).
export class AcquaintancePanelParams extends PanelParams {
@rw sendButtonText: string;
@r standardCssClass?: string = "acquaintance-panel";
@rw services?: $UrlStore & $RequestManager; (1)
}
1 | Объявляем, что для ЭУ необходимы сервисы UrlStore и RequestManager |
После этого, можно обращаться к этим сервисам через this.state.services
:
sendToAcquaintance() {
var url = this.state.services.urlStore.urlResolver.resolveUrl("SendToAcquaintance", "LayoutBusinessProcess");
var data = { ... };
return this.state.services.requestManager.post(url, JSON.stringify(data));
}
Т.к. services
является обычным параметром, его можно передать элементу управления и при этом, если требуется, переопределить сервисы, с которыми работает элемент управления:
let layout = layoutManager.cardLayout;
let myServices = layout.params.services.clone();
myServices.requestManager = new MyRequestManager();
layout.controls.acquaintancePanel1.params.services = myServices;
Не следует модифицировать существующий контейнер для подмены сервисов, т.к. это приведёт к изменению сервисов на уровне всего приложения. |