具有动态参数的Angular工厂提供程序

时间:2019-03-21 14:25:52

标签: angular typescript firebase dependency-injection angular7

我具有以下提供程序工厂,用于动态计算的Firebase Firestore路径:

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内正常工作,但在为产品转译时,我得到: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 个答案:

答案 0 :(得分:0)

我通过重构解决了这个问题。希望对某人有用。

  1. Angular docs on DI提供了有关如何使用注射器的大量文档,这在测试中也很有用。
  2. 但是,我的确切用例没有描述,因为DI在转换时需要定义参数。将此与例如在上下文启动时可以进行值注入的Spring。愚蠢的老我以为这里是一样的,但是当然,即使Spring在启动时也需要定义bean。
  3. 因此,当编译器(包括aot)无法确定参数的来源时,它将引发错误。

如果有人感兴趣,这就是我重构会议服务的方式:

@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);