-3

I have an array. I am running to issues, so...

In my code, I placed the following debugging code:

console.log(this.pages);
console.log(this.pages.length);

The output in Chrome's debug window is like the following. You will see the first one shows a length: 38 but the second console.log shows 0. Why does the second one not show 38 also?

enter image description here


import { Injectable } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2';

@Injectable()
export class SitemapService {
    pagesObservable: FirebaseListObservable<any[]>;
    pages: any[] = [];
    data: string = '';
    constructor(
        protected af: AngularFire, 
        private datePipe: DatePipe,
        private urlPipe: UrlPipe
    ){
        this.pagesObservable = this.af.database.list('/pages', { 
            query: {
                orderByChild: 'sortOrder',
                limitToLast: 100
            },
            preserveSnapshot: true
        })
        this.pagesObservable.subscribe(snapshots => {
            snapshots.forEach(snapshot => {
                this.pages.push(JSON.stringify(snapshot.val()));
            })

        })

    }

    getSitemapData(): string {
        let urlBase = location.protocol + '//' + location.host;
        console.log(this.pages);
        console.log(this.pages.length);
        return (this.data);
    }
}
eat-sleep-code
  • 4,753
  • 13
  • 52
  • 98
  • 2
    — Can you share executable demo/snippet or [JSFiddle](https://jsfiddle.net/) ? [_Create a Minimal, Complete, and Verifiable example_](http://stackoverflow.com/help/mcve) – Rayon May 04 '17 at 05:18
  • try this `typeof this.pages` and see what is printing on console..........and show the code where you have created the array. – abhit May 04 '17 at 05:19
  • Does the little `i` symbol say “Value below was evaluated just now.”? Read [Is Chrome's JavaScript console lazy about evaluating arrays?](https://stackoverflow.com/q/4057440/4642212). – Sebastian Simon May 04 '17 at 05:21
  • 1
    Is the order of the `console.log` same in your code as you have shared in above snippet ? – Rayon May 04 '17 at 05:25
  • 1
    This is a well-known problem related to how devtools displays and then updates what it shows. You can google for it. The initial display (showing an empty array) is the state as of when `console.log` was called. When you open it up by clicking on the down arrow, it shows the **current** state of affairs. –  May 04 '17 at 05:26
  • @lakshay `console.log(typeof this.pages);` reports 'object' – eat-sleep-code May 04 '17 at 05:27
  • @lakshay — ___`typeof []`___ will be `"object"` as well.. How will that conclude anything ? – Rayon May 04 '17 at 05:28
  • @torazaburo but the problem is the second line (where I am trying to just write out the number of items in the array) reports 0 also. That is a problem because i need that number. – eat-sleep-code May 04 '17 at 05:28
  • 2
    @eat-sleep-code — `length` property represent the count of number of items in array at that particular point of time.. It does not update itself when count is changed! – Rayon May 04 '17 at 05:30
  • Then how do I get how many elements in the array? – eat-sleep-code May 04 '17 at 05:31
  • 1
    By doing a `console.log` once it's populated. Most likely you have an async call which populates `this.pages`, and you are issuing the `console.log` statements immediately after the async call, at which point it has not yet completed. If that is the case, try putting the `console.log` statements **inside** the `then` handler or `subscribe` handler or callback or whatever you are using to handle the completion of the async operation. –  May 04 '17 at 05:33
  • 3
    how are you populating your array....show us some code. – abhit May 04 '17 at 05:35
  • Added code. The getSitemapData method is where I need to access the data. – eat-sleep-code May 04 '17 at 05:45
  • Don't do async stuff in your constructor. Unless you store a promise for the result, you will have no way to know when it's done. Wherever you're calling `getSiteMapData` from, it's almost certainly before the async stuff in the constructor has had a chance to finish. –  May 04 '17 at 07:22

2 Answers2

0

Try this one may be its work for you

alert(Object.keys(this.pages).length);
Bhupesh
  • 883
  • 7
  • 16
0

Don't do async stuff like subscribe in your constructor. You will have no way to know when it's done. new is not asynchronous. It does not wait for some some async logic in the constructor to finish before continuing. Wherever you're calling getSiteMapData from, it's almost certainly before the async stuff in the constructor has had a chance to finish. In your case, just set up the observable in your constructor.

You're very confused about how AngularFire list observables work. When you subscribe, you get the data, itself, right there. It doesn't give you snapshots that you have to forEach over and take val() of and do something with. In your case you don't need, and don't want, the preserveSnapshots option, unless you're doing something special.

import { Injectable } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2';

@Injectable()
export class SitemapService {
    pagesObservable: FirebaseListObservable<any[]>;
    pages: any[] = [];
    data: string = '';
    constructor(
        protected af: AngularFire, 
        private datePipe: DatePipe,
        private urlPipe: UrlPipe
    ){
        this.pagesObservable = this.af.database.list('/pages', { 
            query: {
                orderByChild: 'sortOrder',
                limitToLast: 100
            }
        });
    }

    getSitemapData(): string {
      let urlBase = location.protocol + '//' + location.host;
      this.pagesObservable.subscribe(pages => {
        console.log(pages);
        console.log(pages.length);
      });
  }
}

But, you say, I want to keep pages as a property on my component. Before you decide you really want to do that, make sure you can't do the obvious:

<div *ngFor="pagesObservable | async">

which is often a better solution--let Angular do the subscribing (and unsubscribing) for you. If you really want a pages property on your component, then you could do

ngOnInit() {
  this.pagesSubscription = this.pagesObservable.subscribe(pages => this.pages = pages);
}

// MAKE SURE TO DO THIS!
ngOnDestroy() {
  this.pagesSubscription.unsubcribe();
}

But you won't have this.pages until some point in the future. Therefore, if you want to use it in a template, or somewhere else, you'll have to make sure it's been set:

<div *ngIf="pages">I now have the pages!!</div>
  • Thanks. I am pretty new to Angular -- and self taught. I had found the preserveSnapshots example when Googling for answers. I will take a stab at your suggested changes this evening. – eat-sleep-code May 04 '17 at 16:47