Angular 2 array after subscribe has elements but l

2019-07-31 09:14发布

问题:

I'm using Angular 2 and I have noticed an unexpected behaviour.

I have a datasource class, which extends DataSource, with two variables:

private archives = new BehaviorSubject<MyArchive[]>([]);
public archives$: Observable<MyArchive[]> = this.archives.asObservable();
private filteredArchive: MyArchive[];

I update archives this way within the class:

this.archives.next(this.filteredArchive);

Outside in another class I try to subscribe to the observable but it doesn't work:

ngAfterViewInit(): void {

    this.dataSource.archives$.subscribe((archive: Array<MyArchive>) => {

        console.log(archive.length);
        console.log(archive);
        console.log(archive.length);
    }
}

In the console log it prints:

0
<THE ARCHIVE WITH AN OBJECT INSIDE AS IT SHOULD BE, WITH LENGTH = 1>
0

So that I can't iterate on the archive variable because its length is 0. What's going on here?

回答1:

the issue this the value is already emitted before you subscribe. Think of observables like tv stream and your data is like a show on this stream if you open the tv after the show ended (subscribe after you pushed the data) you never see the show. if you want your observable to keep the last value you can use the Scan operator like this :

export const reducer = () =>

   scan<MyArchive[]>((archive, update) => {
    //update your Archive or simply overwrite the value
    archive = update

    return archive

    }, []);

export class datasource {

   archives$ : Observable<MyArchive[]>;

   archives : Subject<MyArchive[]> = new Subject([]);

   update(newMyArchive: MyArchive[]) {
     this.archives.next(newMyArchive);
    }

   constructor(public http: HttpClient) {

   this.archives$ = this.archives.pipe(
     reducer(),
   //use the shareReplay so all your subscribers get the same values
     shareReplay(1)
     );
  this.archives$.subscribe();
}

and you can update the Arcjive useing the update method in the datasource class like:

this.update(filteredArchive)


回答2:

I'm not sure why you're redundantly creating archives$ and archives. You can do it with just a single BehaviorSubject.

I think you should be doing this:

// Create a BehaviorSubject for MyArchive[]
public archives$: BehaviorSubject<MyArchive[]> = new BehaviorSubject<MyArchive[]>([]);
// Create a private filtered Array for MyArchive.
private filteredArchive: MyArchive[];

And then,

this.archives$.next(this.filteredArchive);


回答3:

When you use this statement:

console.log(archive);

a reference to the archive object is shown in the console (see the MDN documentation). The content of the object can be different when you inspect it in the console (by clicking on the arrow) from what it was when the console.log statement was executed.

In order to see what the actual content is at a specific moment, you should output a string representation of the object:

console.log(archive.toString());
console.log(JSON.stringify(archive));

You can expermiment with this stackblitz to see the various console outputs.



回答4:

I solved the problem. I forgot that this.filteredArchive is updated by an HTTP GET request. I was trying to push data within this.filteredArchive using forEach, but I forgot to call then() when all the data are received. Hence I was trying to access, using subscribe, a result that was not yet ready.

Thanks everyone for the replies!