Переключение зависимости внедрения между реализациями
У меня есть приложение, которое работает в двух разных режимах: демонстрационный и обычный. Демо-режим используется только тогда, когда пользователь не имеет учетной записи и хочет только попробовать приложение, чтобы увидеть, как оно работает.
Теперь предположим, что у нас есть сервис, который используется в приложении. Например, сервис для извлечения Account
и аутентифицировать пользователя:
abstract class AccountService {
authenticate(username: string, password: string): Observable<Boolean>;
getAccount(): Observable<Account>;
}
Для этого сервиса у нас есть две реализации, по одной для каждого режима. AccountServiceImpl
в обычном режиме выполняет HTTP-запрос к какому-либо серверу для получения учетной записи. В то время как AccountServiceDemoImpl
просто возвращает жестко закодированный.
Предположим, у нас есть AccountComponent
компонент, который показывает имя учетной записи пользователю. Для этого компонента не имеет значения, какую из реализаций мы используем, поэтому мы просто внедряем AccountService
в своем конструкторе.
Все хорошо, и мы можем легко использовать любую из реализаций для заполнения AccountComponent. Теперь мы хотим иметь возможность переключаться между этими реализациями во время выполнения, когда пользователь нажимает кнопку на странице входа в систему. Поэтому я написал провайдера фабрики, чтобы определить, какую реализацию AccountService использовать.
{provide: AccountService, deps[AccountServiceImpl, AccountServiceDemoImpl], (regularService, demoService) => demoModeActivated ? demoService : regularService}
Как вы можете себе представить, AccountService
также вводится на странице входа в систему, чтобы иметь дело с аутентификацией пользователя. Что означает, что фабрика уже была оценена. Теперь проблема в том, что результат этой оценки кэшируется в Angular. Таким образом, когда демонстрационный режим активирован, и пользователь переходит к AccountComponent
, Настоящий AccountServiceImpl
будет введен снова.
Есть ли способ очистить кэш DI (для определенного набора токенов DI)? Или я должен попытаться разобраться с этой функцией по-другому? (Например, напишите другую реализацию AccountService
что делегирует либо AccountServiceImpl
или AccountServiceDemoImpl
.)
1 ответ
Первоначально может показаться, что заводской поставщик решит это за вас. К сожалению, заводские револьверы являются синхронными, и вы полагаетесь на асинхронный вызов, чтобы определить, какую службу предоставлять.
Вместо этого вы можете использовать Resolve guard. В методе разрешения вы должны вернуть наблюдаемое, вызывающее метод auth, а затем сопоставленное с обновлением любой службы. И затем вызывает оператор first(), чтобы завершить наблюдаемое после его первого запуска.
Это решает, что охрана, кажется, была разработана, чтобы возвратить простые структуры DTO, а не услуги. Но это работает.
Вместо того, чтобы обновлять экземпляры, вы можете внедрить обе службы в Resolve Guard и сопоставить их с любой из них, если вы хотите, чтобы эта служба оставалась одиночной.
export class AccountServiceResolver implements Resolve<AccountService> {
constructor(private accountService: AccountService, demmoService: AccountDemoService) { }
resolve(): Observable<AccountService> {
return this.accountService.getAccount().map(account => {
if (account.real) {
return this.accountService;
} else {
return this.demoService;
}
}).first();
}
}
Тогда получите это в своем компоненте:
ngOnInit(route: ActivatedRoute) {
const service = route.data[0];
}