Search code examples
arraysangulartypescriptobservablejson-server

Array of Observable is empty when loading the page but has content later on?


I'm trying to implement ngx-infinite-scroll. I want to take the first 5 posts out of my "posts" array and put them into "shownPosts" array at the start and then when the user keeps scrolling down I want it to add another 5 but I ran into a problem where my "posts" array is empty while the side is still loading (ngOnInit).

If I check the array with another function (checkPosts) later it's got all of the Posts. Why is that?

Console logs when website loads:

In constructor, this.posts.length: 0
In ngOnInit, this.posts.length: 0
ERROR TypeError: Cannot read properties of undefined (reading 'title')       core.mjs:6485 
    at HotComponent.ngOnInit (hot.component.ts:37:70)
    at callHook (core.mjs:2542:1)
    at callHooks (core.mjs:2511:1)
    at executeInitAndCheckHooks (core.mjs:2462:1)
    at refreshView (core.mjs:9499:1)
    at refreshComponent (core.mjs:10655:1)
    at refreshChildComponents (core.mjs:9280:1)
    at refreshView (core.mjs:9534:1)
    at refreshEmbeddedViews (core.mjs:10609:1)
    at refreshView (core.mjs:9508:1)

Console log after I run "checkPosts()" through a button once the site has loaded:

In checkPosts, this.posts.length: 32
In checkPosts, this.posts[0].title: This is the first posts title.

hot.component.ts:

import ...

@Component({
...
})

export class HotComponent implements OnInit {
  posts: Post[] = [];
  shownPosts: Post[] = [];
  normalDrawerShown: boolean = true;
  subscription!: Subscription;
...

constructor(private postService: PostService, private uiService: UiService) {
  this.subscription = this.uiService
  .onToggleNormalDrawer()
  .subscribe((value) => (this.normalDrawerShown = value));

  console.log("In constructor, this.posts.length: "+ this.posts.length);
}

ngOnInit(): void {
  this.postService.getPosts().subscribe((posts) => (this.posts = posts));

  console.log("In ngOnInit, this.posts.length: "+ this.posts.length);
  console.log("In ngOnInit, this.posts[0].title: " + this.posts[0].title);
}

checkPosts():void{
  console.log("In checkPosts, this.posts.length: "+ this.posts.length);
  console.log("In checkPosts, this.posts[0].title: " + this.posts[0].title);
}
...
}

Post.ts:

export interface Post {
  id?: number;
  title: string;
  fileName: string;
  url: string;
  section: string;
  postTime: string;
  upvotes: number;
  downvotes: number;
  comments: number;
}

post.service.ts:

getPosts():Observable<Post[]> {
  return this.http.get<Post[]>(this.apiUrl);
}

Solution

  • Http request to backend api take time.

    this line of code:

    this.postService.getPosts().subscribe((posts) => (this.posts = posts));
    

    ...means that when observable created here:

    this.http.get<Post[]>(this.apiUrl);
    

    ...emits value (therefore when http request return some value) then take that value and assign it to this.posts. And since http requests take some time, that value is not showed when you try to console.log it right after subscription.