繁体   English   中英

管理 Angular 服务和组件之间的订阅

[英]Manage subscriptions between Angular services and components

我正在开发一个 Web 应用程序,其中多个组件共享相似的功能。 为了避免不同组件中的代码重复,我创建了一个服务来托管这些功能。

但是,我不确定如何管理共享函数的订阅,因为它们不再是每个组件的一部分,仍然需要在服务中订阅。 理想情况下,我根本不会订阅服务,只订阅组件,但功能的性质及其目的不允许我这样做。 这些是嵌套在另一个中的 AngularFire 函数。 此结构的目的是浏览数据库的不同级别并动态填充对象。 然后使用该对象生成主表的标题部分。 级别数(列和堆叠行)取决于数据库中存在的数据,具体取决于正在查询的数据库节点。 这不是我最喜欢的处理方式,所以如果你想一个更干净的方式来做到这一点,无论如何。 请记住,实际代码比下面的示例要复杂一些。

例如,此代码生成以下对象,其对应的标题部分如下图所示:

object = {

    title: 'PW100',
    sections: {
      section1: {
        title: 'HIGH PRESSURE ROTOR',
        sections: {
          section1: {
            title: 'HP TURBINE',
            sections : {
              section1: {title: 'ASS'},
              section2: {title: 'GRD'},
              section3: {title: 'BAL'}
            }
          },
          section2: {
            title: 'HP ROTOR',
            sections: {
              section1: {title: 'ASS'},
              section2: {title: 'BAL'}
            }
          }
        }
      },
      section2: {
        title: 'LOW PRESSURE ROTOR',
        sections: {
          section1: {
            title: 'LP TURBINE',
            sections : {
              section1: {title: 'ASS'},
              section2: {title: 'RIV'},
              section3: {title: 'GRD'},
              section4: {title: 'BAL'}
            }
          },
          section2: {
            title: 'LP ROTOR',
            sections: {
              section1: {title: 'ASS'},
              section2: {title: 'BAL'}
            }
          }
        }
      },
      section3: {
        title: 'POWER TURBINE PACK',
        sections: {
          section1: {
            title: '3RD STAGE TURBINE',
            sections : {
              section1: {title: 'ASS'},
              section2: {title: 'RIV'},
              section3: {title: 'GRD'},
              section4: {title: 'BAL'}
            }
          },
          section2: {
            title: '4TH STAGE TURBINE',
            sections : {
              section1: {title: 'ASS'},
              section2: {title: 'RIV'},
              section3: {title: 'GRD'},
              section4: {title: 'BAL'}
            }
          },
          section3: {
            title: 'PT PACK',
            sections: {
              section1: {title: 'ASS'},
              section2: {title: 'BAL'}
            }
          }
        }
      }
    }

  };

在此处输入图片说明

我以前使用过的效果很好:

组件 A 和 B 过去具有以下结构:

export class ComponentXXClass implements OnInit, OnDestroy {

  // Variable used to unsubscribe in ngOnDestroy()
  $unsubscribe = new Subject();

  constructor(private afs: AngularFirestore) {}

  ngOnInit(): void {
    this.functionA().then(result => {
      // Use promise's result (which is an object) to generate the table's headers section
    });
  }

  ngOnDestroy(): void {
    this.$unsubscribe.next();
    this.$unsubscribe.complete();
  }

  functionA() {

    const objectToPopulateOnEachLevel = {};

    return new Promise((resolve) => {

      this.afs.collection('collectionName')
      .snapshotChanges()
      .pipe(takeUntil(this.$unsubscribe))
      .subscribe(level1VariableRequiredToAccessLevel2 => {

        level1VariableRequiredToAccessLevel2.forEach(level1Var => {

          // Store relevant information found at this level of the query
          objectToPopulateOnEachLevel['level1'] = level1Var.payload.doc.data()['variableOfInterest'];
    
          this.afs.collection(level1Var.payload.doc.ref.path + '/path1')
          .snapshotChanges()
          .pipe(takeUntil(this.$unsubscribe))
          .subscribe(level2Variable => {

            level2Variable.forEach(level2Var => {

              // Store relevant information found at this level of the query
              objectToPopulateOnEachLevel['level2'] = level2Var.payload.doc.data()['variableOfInterest'];
    
            });

            resolve(objectToPopulateOnEachLevel);

          });
        });
      });
    });
  }
}

代码的公共部分在组件 A 和 B 之间重复(在我的实际场景中还有更多),所以我最终做了这样的事情:

服务 :

export class Service {

  // Variable used to unsubscribe in components' ngOnDestroy()
  $unsubscribe = new Subject();

  constructor(private afs: AngularFirestore) {}

  functionA(parentPath: string, childPath: string) {

    const objectToPopulateOnEachLevel = {};

    return new Promise((resolve) => {

      this.afs.collection(parentPath)
      .snapshotChanges()
      .pipe(takeUntil(this.$unsubscribe))
      .subscribe(level1VariableRequiredToAccessLevel2 => {

        level1VariableRequiredToAccessLevel2.forEach(level1Var => {

          // Store relevant information found at this level of the query
          objectToPopulateOnEachLevel['level1'] = level1Var.payload.doc.data()['variableOfInterest'];
    
          this.afs.collection(level1Var.payload.doc.ref.path + '/' + childPath)
          .snapshotChanges()
          .pipe(takeUntil(this.$unsubscribe))
          .subscribe(level2Variable => {

            level2Variable.forEach(level2Var => {

              // Store relevant information found at this level of the query
              objectToPopulateOnEachLevel['level2'] = level2Var.payload.doc.data()['variableOfInterest'];
    
            });

            resolve(objectToPopulateOnEachLevel);

          });
        });
      });
    });
  }
}

组件 A 和 B:

export class ComponentXXClass implements OnInit, OnDestroy {

  constructor(private _service: Service) {}

  ngOnInit(): void {
    this._service.functionA('parentPathString', 'childPathString').then(result => {
      // Use promise's result (which is an object) to generate the table's headers section
    });
  }

  ngOnDestroy(): void {
    this._service.$unsubscribe.next();
    this._service.$unsubscribe.complete();
  }

}

这有效,但不完全。 我的意思是数据正确显示,但似乎即使我没有退出组件 A(例如),订阅似乎并不活跃。 在组件 A 中,我尝试在服务中订阅的节点处更新数据库中的变量。 预期的行为是让订阅触发并向组件 A 发出一个新值。例如,在上面的示例中(对象及其相应的标题显示在图像中),如果我进入数据库并更改“HIGH PRESSURE ROTOR”"NO PRESSURE ROTOR" ,我希望订阅在服务中触发,通过在承诺中重新创建对象来获取更改,并将其重新发送给组件 A,然后组件 A 将重新呈现表头。 然而,这不会发生。 我还尝试删除“取消订阅”部分,看看我处理取消订阅的方式是否是原因。 结果是一样的。

有什么想法吗?

谢谢

您必须使用 observables 来管理数据readonly objectPopulatedSubject = new BehaviorSubject({})的状态,您还必须创建一个属性来访问 observable readonly objectPopulated$ = objectPopulatedSubject.asObservable()并且您必须创建一个方法更新主题并为您的订阅发出新值updateObjectPopulated(data: YourType) { this.objectPopulatedSubject.next(data)}并且在您的组件中您应该订阅 objectPopulated$。

同样要管理订阅,您应该遵循以下说明:创建订阅属性objectPopulatedSubscription: Subscription; 然后在你的 ngOnInit this.objectPopulatedSubscription = this.yourservice.objectPopulated$.subscribe(data => {//put here your logic})并在你的 ngOnDestroy 中把this.objectPopulatedSubscription.unsubscribe()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM