0

I have a service, that returns Observables<SubscriberModel[]>.

export interface SubscriberModel {
    email: string;
}

in my component I get subscribers$ in constructor:

public subscribers$: Observable<SubscriberModel[]>;
this.subscribers$ = this.subscribersService.getAll();

and show their in template:

<p *ngFor="let subscriber of subscribers$ | async">{{subscriber.email}}</p>
<button (click)="onDownloadClick()">
    <a [href]="fileUrl" download="file.txt">DownloadFile</a>
</button>

I have to download my subscribers into file:

public data: any;

public onDownloadClick(): void {
    this.subscribers$.pipe(
        map(res => res.map(i => i.email)),
    )
        .subscribe(res => {
            console.log(res) //array of string
            this.data = res;
        });

    console.log(this.data); //undefined
    const blob: Blob =
        new Blob([this.data], {type: 'application/octet-stream'});

    this.fileUrl =
        this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
}

Why in console.log this.data is undefined?

prolina
  • 185
  • 4
  • 14

2 Answers2

4

put your process related service's subscribe's inside. Async functions doesn't stop another process. So code goes on with next line while it is still executed. so outside of async function may not read data due to response of server.

public onDownloadClick(): void {
   this.subscribers$.pipe( map(res => res.map(i => i.email)),)
   .subscribe(res => {
       console.log(res) //array of string
       this.data = res;
       console.log(this.data); //undefined
       const blob: Blob = new Blob([res], {type: 'application/octet-stream'});       
       this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
    });

}
mr. pc_coder
  • 16,412
  • 3
  • 32
  • 54
2

this.data is assigned data asynchronously. As such, all the dependent statements must be inside the subscription. So the console.log should be inside the subscription

public data: any;

public onDownloadClick(): void {
  this.subscribers$.pipe(
    map(res => res.map(i => i.email)),
  )
  .subscribe(res => {
    console.log(res) //array of string
    this.data = res;
    console.log(this.data);
    const blob: Blob = new Blob([this.data], {type: 'application/octet-stream'});
    this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
  });
}

More info on accessing asynchronous data: https://stackoverflow.com/a/14220323/6513921

ruth
  • 29,535
  • 4
  • 30
  • 57
  • But in this case value in file.txt is undefined and only if I click the second time to download button, the file includes correct value – prolina May 27 '20 at 19:31
  • The other statements were also dependent on the async data. They should be moved inside the subscription as well. I've updated the answer. – ruth May 28 '20 at 06:43