Переключение зависимости внедрения между реализациями

У меня есть приложение, которое работает в двух разных режимах: демонстрационный и обычный. Демо-режим используется только тогда, когда пользователь не имеет учетной записи и хочет только попробовать приложение, чтобы увидеть, как оно работает.

Теперь предположим, что у нас есть сервис, который используется в приложении. Например, сервис для извлечения 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];
}
Другие вопросы по тегам