Угловой заводской провайдер с динамическим параметром - PullRequest
1 голос
/ 21 марта 2019

У меня есть следующая фабрика провайдеров для динамически рассчитанного пути к пожарной базе:

const meetingServiceFactory = (db: AngularFirestore, authService: AuthService ) => {

  const userId = authService.user;
  return new MeetingService('/users/' + userId + '/meetings', db);

};

export let meetingServiceProvider =   { provide: MeetingService,
                                        useFactory: meetingServiceFactory,
                                        deps: [AngularFirestore, AuthService] };

Здесь MeetingService:

@Injectable(
  {
    providedIn: 'root'
  }
)
export class MeetingService extends FirestoreDataService<Meeting> {

  constructor(path: string, db: AngularFirestore) {
    super(path, db);
  }


  setProposalFlag(meetingKey: string) {
    return from(this.db.doc(this.path + `/${meetingKey}`).update({hasProposal: true})
      .then(result => console.log('changed active Flag on meeting: ', result))
      .catch(err => console.log(err)));
  }

  saveMeeting(meeting: Meeting) {

    return of(meeting).pipe(
      map(meetingToSave =>  {
       return  {...meetingToSave, key: this.db.createId() };
      }),
      switchMap( finalMeeting => this.addItemWithKey(finalMeeting.key, finalMeeting) )
    );
  }

  getAll() {
    return this.getItems();
  }
}

Это наследуется от FirestoreDataService<T>, так что яесть тип службы CRUD для повторного использования:

@Injectable()
export class FirestoreDataService<T> {

  protected itemCollection: AngularFirestoreCollection<T>;
  protected items$: Observable<T[]>;
  protected snapshot$: Observable<DocumentChangeAction<T>[]>;
  protected state$: Observable<DocumentChangeAction<T>[]>;

  constructor(protected path: string, protected db: AngularFirestore) {
    this.itemCollection = this.db.collection<T>(path);
    this.items$ = this.itemCollection.valueChanges();
    this.snapshot$ = this.itemCollection.snapshotChanges();
    this.state$ = this.itemCollection.stateChanges();
  }

  getItems(): Observable<T[]> {
    return this.items$;
  }

  getItemWithKey( key: string): Observable<T> {
    // used to use stateChanges here.
    // as the doc states, this only emits recent changes,
    // so if we subscribe in an async pipe, it will not reemit and subsequent observables will be empty.
    return this.itemCollection.doc<T>(key).valueChanges();
  }

  addItem(data: T) {
    return from(this.itemCollection.add(data)
                                          .catch(err => console.log('Error while adding item: ', err)));
  }

  addItemWithKey(key: string, data: T) {
    return from(this.itemCollection.doc(key).set(data)
                                          .catch(err => console.log('Error while adding item: ', err)));
  }

  deleteItem(key: string) {
    return from(this.itemCollection.doc(key).delete()
                                          .catch(err => console.log('Error while deleting item: ', err)));
  }
}

, но это, очевидно, зависит от времени выполнения решения по userId.Кажется, работает нормально в ng serve, но при переносе для prod я получаю: ERROR in : Can't resolve all parameters for FirestoreDataService in /web-client/src/app/core/services/firestore-data.service.ts: (?, [object Object]).

Я предполагаю, что это потому, что я не знаю точный путь во время компиляции.Это так?Если нет, что может быть проблемой с этой настройкой?И как бы это исправить / улучшить?Я бегу Angular 7 в данный момент.Спасибо за помощь!

1 Ответ

1 голос
/ 05 апреля 2019

Я решил проблему с помощью рефакторинга. Вот в надежде, что это кому-нибудь пригодится.

  1. Угловые документы по DI предоставляют обширную документацию о том, как использовать инжектор, что также полезно при тестировании.
  2. Однако нет описания для моего точного варианта использования, потому что DI должен иметь определенные параметры во время переноса. Сравните это с такой структурой, как, например, Весна, где вы могли бы сделать внедрение значения, когда контекст запускается. Глупый старый я подумал, что здесь будет то же самое, но, конечно, даже Spring должен при запуске определять бины.
  3. Следовательно, когда транспортер (включая aot) не может решить, откуда должен поступить параметр, он выдает ошибку.

Если кому-то интересно, вот как я реорганизовал свой MeetingService:

@Injectable(
  {
    providedIn: 'root'
  }
)
export class MeetingService extends FirestoreDataService<Meeting> {

   constructor(authService: AuthService,
          db: AngularFirestore) {
     const path = `/users/${authService.user}/meetings`;
     super(path, db);
     this.auth = authService;
   }

...

}

Обратите внимание, что это работает только потому, что внедрение служб таким образом, параметры являются частными для конструктора, в противном случае первый вызов должен быть super(path, db);

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...