Search code examples
angulartypescriptangularfire2rxjs6

How do I properly initialize and assign a observable using angulafire2?


I'm using Angular 6 and Rxjs 6.

The following code will throw undefined at the ListFormsComponent over and over and over until the Observable is assigned by getForms() at which it will then display the data. The getForms() is called by the navigation service when attempting to navigate to ListFormsComponent page.

FormService.ts

export class FormService {

  public forms$: Observable <Array<Form>> ;
  public assignedForms$: Observable <Array<Form>> ;
  public form$: Observable <Form>;
  constructor(
    private afs: AngularFirestore,
    private authService: AuthService,
  ) {}

  getForms() {
    let zippedData = zip(this.authService.user$);
    zippedData.subscribe(data => {
      this.forms$ = this.afs.collection('networks')
        .doc(data[0].activeNetworkProfile.id)
        .collection('companyProfiles')
        .doc(data[0].activeCompanyProfile.id)
        .collection<Forms>('inspectionForms')
        .valueChanges();
    })
  }

}

ListFormsComponent.ts

export class ListFormsComponent implements OnInit {
  public forms: Forms[];
  constructor(private formService: FormService, private navigateService: NavigationService) {

    this.formService.forms$.subscribe(forms => { //<---Error here this.formService.forms$ is undefined
      this.forms = forms;
      console.log("Forms inside component");
      console.log(this.forms);
    })
  }
}

ListFormsComponent.html

<mbsc-listview [options]="listSettings" style="display:none">
  <mbsc-listview-item (click)="viewDetails(form.id)" *ngFor="let form of forms">
    <h3 class="mbsc-lv-txt">{{form.name}}</h3>
    <p class="mbsc-lv-txt">{{form.lastEdited | date: 'medium'}} </p>
  </mbsc-listview-item>
</mbsc-listview>

NavigationService.ts

navigateForms() {
  this.formService.getForms();
  this.navigateService.navigate('forms');
}

So I then decided to initialize the observable by doing the following code(see below) in the constructor of FormService.ts and it stops the errors but It does not trigger the subscription in the ListFormsComponent.ts. I'm assuming when valueChanges() returns the Observable to forms$ it breaks rest of the subscriptions.

this.forms$ = new Observable<Array<Forms>>();

Solution

  • So the issue is in this function:

    navigateForms() {
      this.formService.getForms();
      this.navigateService.navigate('forms');
    }
    

    You've called this.formService.getForms(); which will initialize forms$ on FormService. But simultaneously, you're also navigating to forms which will render the ListFormsComponent. In your ListFormsComponent you're doing formService.forms$ which might not have initialized at that point. Hence you're getting it as undefined.

    Instead of using the forms$, I would have simply returned the Observable value from the getForms method and I would have called it in the ListFormComponent's ngOnInit method:

    export class FormService {
    
      public forms$: Observable <Array<Form>> ;
      public assignedForms$: Observable <Array<Form>> ;
      public form$: Observable <Form>;
      constructor(
        private afs: AngularFirestore,
        private authService: AuthService,
      ) {}
    
      getForms() {
        return zip(this.authService.user$).pipe(
          flatMap(data => {
            return this.afs.collection('networks')
              .doc(data[0].activeNetworkProfile.id)
              .collection('companyProfiles')
              .doc(data[0].activeCompanyProfile.id)
              .collection<Forms>('inspectionForms')
              .valueChanges();
          })
        );
      }
    
    }
    

    Then in the ListFormsComponent:

    export class ListFormsComponent implements OnInit {
      public forms: Forms[];
      constructor(
        private formService: FormService, 
        private navigateService: NavigationService
      ) { }
    
      ngOnInit() {
        this.formService.getForms().subscribe(forms => {
          this.forms = forms;
          console.log("Forms inside component");
          console.log(this.forms);
        });
      }
    
    }
    

    Also, I won't need to call getForms from navigateForms now. So I could get rid of that:

    navigateForms() {
      this.navigateService.navigate('forms');
    }