Angular 2 array after subscribe has elements but l

2019-07-31 09:11发布

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?

4条回答
我命由我不由天
2楼-- · 2019-07-31 09:50

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)
查看更多
欢心
3楼-- · 2019-07-31 09:50

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);
查看更多
再贱就再见
4楼-- · 2019-07-31 09:51

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.

查看更多
成全新的幸福
5楼-- · 2019-07-31 10:05

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!

查看更多
登录 后发表回答