Nick
Nick

Reputation: 480

InvalidPipeArgument: '' for pipe 'AsyncPipe' at invalidPipeArgumentError

I'm trying to extract a string value from the Observable using the pipe and map, how ever I always got in the end an empty string. I hope that someone can help me to understand the reason behind this issue.

I have an http service that get a file from the back-end:

getFiles(url: string, idProject: number, docProject: string): Observable<any> { 
return this.http.get(`${this.hostUrl}${url}/${idProject}/${docProject}`); 
  }

I call the getFiles(...) like that:

  showFile = false;
  fileUploads: Observable<string[]>;
.
.
.
showFiles(enable: boolean) {
    this.showFile = enable;

    if (enable) {
this.uploadService.getFiles('/projets', this.service.getProjet().idProjet,
  'documentProjets/pv/'//+ td.nomTypeDoc +'/getallfiles')
            .pipe( 
                  map( response => {
                  return response.slice(
                         response.indexOf("files"), 
                         response.lastIndexOf("?"))
                         })).subscribe(
                           (data) => {
                            this.fileUploads=data
                            console.log(data)},
                           (error) => console.log(error));
}
}

The result before the Map is:

http://localhost:8080/api/v1/pv/files/file1.jpg?projetId=563, http://localhost:8080/api/v1/pv/files/file2.jpg?projetId=563 

and after the MAp is: an empty array.

The HTML:

<button class="button btn-info" *ngIf='showFile' (click)='showFiles(false)'>Hide Files</button>

<button class="button btn-info" *ngIf='!showFile' (click)='showFiles(true)'>Show Files</button> 


<div [hidden]="!showFile">
  <div class="panel panel-primary">
    <div class="panel-heading">Liste des fichiers</div>

    <div *ngFor="let file of fileUploads | async">  
      <div class="panel-body">
        <app-details-upload [fileUpload]='file'></app-details-upload>
      </div>
    </div>
  </div>
</div>

I don't know why I get as result an empty value ' ' and this is the error from my consol:

ListUploadComponent.html:3 ERROR Error: InvalidPipeArgument: '' for pipe 'AsyncPipe' at invalidPipeArgumentError (common.js:3981) at AsyncPipe.push../node_modules/@angular/common/fesm5/common.js.AsyncPipe._selectStrategy (common.js:4590) at AsyncPipe.push../node_modules/@angular/common/fesm5/common.js.AsyncPipe._subscribe (common.js:4580) at AsyncPipe.push../node_modules/@angular/common/fesm5/common.js.AsyncPipe.transform (common.js:4562) at Object.eval [as updateDirectives] (ListUploadComponent.html:10) at Object.debugUpdateDirectives [as updateDirectives] (core.js:11054) at checkAndUpdateView (core.js:10451) at callViewAction (core.js:10692) at execComponentViewsAction (core.js:10634) at checkAndUpdateView (core.js:10457)

Thank you in advance.

Upvotes: 4

Views: 3417

Answers (1)

Andrew Allen
Andrew Allen

Reputation: 8062

The async pipe takes an Observable and subscribes for you in the template and unsubscribes for you on component destruction. Keep to the convention of using a $ on the end of observable streams variables.

The following

<div *ngFor="let v of values$ | async">
  {{v}}
</div>

is equivalent to

<div *ngFor="let v of values">
  {{v}}
</div>

where in your ts file you do this

this.values$.subscribe(values => this.values = values)


In your example you can either remove the async pipe from the template or remove the subscription and assign the stream.

this.fileUploads$ = this.uploadService.getFiles('/projets', this.service.getProjet().idProjet,
  'documentProjets/pv/'//+ td.nomTypeDoc +'/getallfiles')
            .pipe( map( response => {
                  return response.slice(
                         response.indexOf("files"), 
                         response.lastIndexOf("?"))
                         }))

If you need to console.log for debugging use the tap RxJS pipeable operator.


Update - after debugging steps

The tap operator does not affect the stream (it can be used for side effects like navigation or logging) so it tells you exactly what the next operator in the pipe stream is getting...an array of strings. So type for this as follows...

this.fileUploads$ = this.yourService.pipe(
    // tap(console.log),
    map((responseUrls: string[]) => {
        return responseUrls.map((url: string) => {
           // some function that 
           // uses string methods
           // or regex
           // to return what you
           // want e.g.
           return url.slice(
               url.indexOf(‘files’),
               url.indexOf(‘?’)
           )
        })
    })
    // }),
    // tap(console.log)
)

Upvotes: 3

Related Questions