angularrxjsangular2-observables

Angular Observable: Unable to read property Length inside Subscribe


I've recently started developing using Angular, and I'm still in learning mode. So, I have this dilemma, which I need to access the "Length" property of an array inside of .Subscribe of an Observable; which I'm currently getting undefined. Strange, because I can see the Array values, but when calling Length I get undefined.

This is my code...

category: Observable<Category>;
categorySubject = new Subject<Category>();
categorySubscription: Subscription;


  
constructor(private db: AngularFirestore) { }

retreivingItems() {
this.fetchCategoryItems("").subscribe((items) => {
  console.log(items.properties); // Properties are available
  console.log('properties[] length = ' + items.properties.length); // output is still undefined
})


}


fetchCategoryItems(category: string) {
  // Get data from firebase    
  return this.db
  .collection('category')
  .doc(category)
  .get()
  .pipe(      
    map((doc) => ({
      id: doc.id,
      ...doc.data() as Category
    }))     
  );
}

}

// Category & Property Model
export interface Category {
    category_name: string,
    properties: Array<Property>
}

export interface Property {
    field_name: string,
    field_tpye: string
}

code

I have also tries using a Subject to wrap the Observable, but I still get undefined.

My guess is that on the Subscribe, the category values hasn't still finished gathering all the streamed data... so how can I achieve this?

Output: undefined_output


Solution

  • A few things to clear out first:

    1. "Observables" emit data when you "subscribe" to it. When you call db.collection().doc().get(), you basically "prepare" the observable to emit data.
    2. Observables which get "completed" at any point in future (i.e. network calls), those are completed immediately. So you don't need to "unsubscribe" those observables.

    Now, in your code, you have defined fetchCategoryItems to store the observable in the category property, which looks unnecessary. Since you are calling the Firestore database, it's a network call and is immediately completed upon successful response. So, you can modify this class in question (assuming it's a service provider) like the following:

    @Injectable({ providedIn: 'root' })
    export class CategoryService {
      constructor(private readonly db: AngularFirestore) {}
      fetchItems(category: string) {
        return this.db.collection('category').doc(category).get().pipe(map((doc) => ({ id: doc.id, ...doc.data() })));
      }
    }
    

    Notice how we have set it up to return an observable from fetchItems method. All is left now is to subscribe to this observable and get the data. In the component file, you can do:

    @Component({ /* ... */ })
    export class CategoryComponent {
      constructor(private readonly categoryService: CategoryService) {}
      onFetch() {
        this.categoryService.fetchItems().subscribe((items) => {
          console.log(items.properties); // not `undefined`
          console.log(items.properties.length); // it should be `4` according to your question
        });
      }
    }