【问题标题】:Angular Factory Provider with a Dynamic Parameter具有动态参数的 Angular 工厂提供程序
【发布时间】:2019-03-21 14:25:52
【问题描述】:

我有以下提供程序工厂,用于动态计算的 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&lt;T&gt;,所以我有一种 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。感谢您的帮助!

【问题讨论】:

  • 您是否尝试从FirestoreDataService 中删除@Injectable() 装饰器?因为无论如何你都是自己实例化类实例,没有 Angular DI。
  • @BorysKupar。是的,试过了。没用。
  • 这种情况下显示的错误是一样的吗?
  • 你如何使用meetingServiceProvider
  • @BorysKupar 是的,同样的错误。我将它注入到我的模块中:providers: [ FormBuilder, MeetingForm, meetingServiceProvider, GeocodingService ]

标签: angular typescript firebase dependency-injection angular7


【解决方案1】:

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

  1. Angular docs on DI 提供有关如何使用注射器的大量文档,这在测试中也很有用。
  2. 但是,我的具体用例没有描述,因为 DI 需要在编译时定义参数。将其与类似的框架进行比较,例如当上下文启动时,您可以在 Spring 中进行值注入。愚蠢的老我认为这在这里是一样的,但是当然,即使是 Spring 在启动时也需要定义 bean。
  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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    • 1970-01-01
    • 2013-11-27
    • 1970-01-01
    相关资源
    最近更新 更多