У меня есть класс angular, который представляет форму. Я хочу, чтобы иметь возможность создавать экземпляры нескольких экземпляров этого класса с помощью конструктора.
Конструктор принимает несколько аргументов, представляющих свойства этой формы.
Внутри моего класса я хочу использовать сервис, который предоставляет возможность рисовать фигуры на карте. Можно ли внедрить эту службу в мой класс и по-прежнему использовать конструктор стандартным способом.
Итак, я хочу сделать что-то вроде ниже и angular автоматически разрешить вложенную зависимость.
Ответ 2
Вот два других возможных способа достижения желаемого или очень сходного результата.
Первый подход - использование менеджера для ваших объектов или объектов без обслуживания
У вас есть один или несколько сервисов factory, которые/отвечают за создание объектов.
Это означает, что он может предоставлять необходимые отпечатки дальше объектам и не требует, чтобы вы передавали их сами.
Например, предположим, что у вас есть сущности как иерархия классов:
abstract class Entity { }
class SomeEntity extends Entity {
...
}
Затем вы можете иметь EntityManager, который является сервисом, и может создавать объекты:
@Injectable() // is a normal service, so DI is standard
class EntityManager {
constructor(public http: Http) { } // you can inject any services now
create<E extends Entity>(entityType: { new(): E; }): E {
const entity = new entityType(); // create a new object of that type
entity.manager = this; // set itself on the object so that that object can access the injected services like http - one can also just pass the services not the manager itself
return entity;
}
}
Вы также можете иметь параметры конструкции, если хотите (но у них не будет никакой информации о типе, поскольку create
должен работать со всеми типами сущностей):
class SomeEntity extends Entity {
constructor(param1, param1) { ... }
}
// in EntityManager
create<E extends Entity>(entityType: { new(): E; }, ...params): E {
const entity = new entityType(...params);
...
}
Теперь ваши сущности могут объявить менеджера:
abstract class Entity {
manager: EntityManager;
}
И ваши сущности могут использовать его для выполнения любых действий:
class SomeEntity extends Entity {
doSomething() {
this.manager.http.request('...');
}
}
Теперь каждый раз, когда вам нужно создать объект/объект, вы используете этот менеджер. EntityManager
нужно вводить сам, но сущности - это свободные объекты. Но все angular код начнется с контроллера или службы или, таким образом, можно будет ввести менеджера.
// service, controller, pipe, or any other angular-world code
constructor(private entityManager: EntityManager) {
this.entity = entityManager.create(SomeEntity);
}
Этот подход также может быть адаптирован к произвольным объектам. Вам не нужна иерархия классов, но с typescript это работает лучше. Также имеет смысл иметь некоторый базовый класс для ваших объектов, так как вы можете повторно использовать код и этот способ старой моды, особенно в области/объектно-ориентированном подходе.
PROS. Этот подход более безопасен, поскольку он все еще находится на полной иерархии DI и должен быть менее нежелательный побочный эффект.
CONS. Недостатком является то, что вы никогда не сможете использовать new
снова и не сможете получить доступ к этим службам в произвольном коде. Вы всегда должны полагаться на DI и на своих factory/фабриках.
Второй подход - h4ckz0rs
Вы создаете службу, предназначенную для получения (через DI) услуг, которые вам нужны в объектах.
Эта часть очень похожа на первый подход, просто эта служба не является factory. Вместо этого он передает внедренные службы в объект, который определен за пределами этого класса в другом файле (объяснение после). Например:
...
import { externalServices } from './external-services';
@Injectable()
export class ExternalServicesService {
constructor(http: Http, router: Router, someService: SomeService, ...) {
externalServices.http = http;
externalServices.router = router;
externalServices.someService = someService;
}
}
Объект, который будет содержать сервисы, определяется в собственном файле следующим образом:
export const externalServices: {
http,
router,
someService
} = { } as any;
Обратите внимание, что службы не используют информацию о типе (это недостаток, но необходим).
Затем вы должны убедиться, что ExternalServicesService
вводится один раз. Лучше всего использовать основной компонент приложения:
export class AppComponent {
constructor(..., externalServicesService: ExternalServicesService) {
Наконец, теперь вы можете использовать службы в любом произвольном объекте в любой момент после создания основного компонента приложения.
import { externalServices } from '../common/externalServices' // or wherever is defined
export class SomeObject() {
doSomething() {
externalServices.http().request(...) // note this will be called after ng2 app is ready for sure
}
}
Обратите внимание, что вы не сможете вызвать какую-либо из этих служб в коде класса или в объектах, не созданных после создания приложения. Но в типичном приложении это никогда не понадобится.
Теперь несколько пояснений об этой странной установке:
Зачем использовать объект externalServices
в отдельном файле вместо одного и того же файла или просто сохранять службы самого класса (как статические атрибуты) и почему службы непечатаны?
Причина в том, что при расширении кода, например. через angular -cli/webpack с режимом --prod
, он, скорее всего, получит циклические зависимости, которые не могут быть правильно решены, и вы получите уродливые ошибки, которые трудно найти -
Я уже прошел через это:).
Ошибка формы
Невозможно прочитать свойство 'prototype' из undefined
видно только при запуске с флагом --prod
, намекает на то, что зависимости не устранены правильно.
Намного лучше убедиться, что ExternalServicesService
зависит только от externalServices
, чтобы передать экземпляры службы, и приложение только вводит ExternalServicesService
один раз (например, в ваш основной AppComponent), тогда все произвольные коды/объекты будут использовать только externalServices
для получения услуг.
Таким образом, любому такому коду нужно будет импортировать externalServices
, у которого нет дальнейших отпечатков (потому что службы также не печатаются). Если бы они импортировали ExternalServicesService
, он бы импортировал все остальное и не смог бы статически ставить двунаправленные отпечатки. И это становится серьезной проблемой в ng2/webpack при наборе для prod.
То же самое произойдет, если мы будем использовать типы для сервисов, потому что для этого потребуется imports
.
ПРОФИ: Этот подход проще использовать после установки, и вы можете использовать new
. В принципе, любой файл кода может импортировать externalServices
и иметь мгновенный доступ к тем службам, которые вы хотите открыть таким образом.
CONS: Недостатком является хакерская настройка и возможные проблемы, вызванные циклическими нажатиями. Это также более чувствительно, так как вы не можете быть уверены, что externalServices
имеет эти услуги сразу. Они будут определены только после запуска приложения ng2 и сначала будет введена ExternalServicesService
. Недостатком является также то, что у вас больше нет информации о типе этих сервисов.
PS: Я не уверен, почему эта тема не более популярна.
Например, являясь фанатом ориентированного на домен проектирования, наличие мощных объектов (например, методов, направленных на вызовы REST или взаимодействия с другими службами) имеет важное значение, и это ограничение всегда затрудняло.
Нам пришлось преодолеть это ограничение как в angularjs, так и теперь снова в Angular2 +, поскольку он, кажется, еще не рассматривается в библиотеке.