Reputation: 480
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
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.
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