0

I have the following problem:

I want to make a table with entries (Obj). And some of them have a file attribute. If they have a file attribute (entry.file) I want to make a backend call to get the url of that file:

public getFileURL(archiveID: string, documentID: string, sysID: string){
    const request: FileRequest = {
      archiveID: archiveID,
      documentID: documentID,
      sysID: sysID
    };
    this.fileService.file(request).subscribe(response => {
      if (response) {
        return response;
      }
    })
}

This is called like: getFileURL(entry.file.archiveID, entry.file.documentID, entry.file.sysID) And it should return an Observable, so I can check if i got a backend response.

<tr *ngFor="let entry of period.claims; let i = index">
...
<td>
  <div *ngIf="entry.file">
    <div *ngIf="fileClientService.getFileURL(entry.file.archiveID, entry.file.documentID, entry.file.sysID) | async as file; else loading">
      <a target="about:blank" class="download" (click)="clickLink(file)"></a>
    </div>
    <ng-template #loading let-file>loading..</ng-template>
  </div>
</td>

All I want is to display "loading" until the url is loaded and then display the a-tag. Also, the url parameter coming back from the backend could be empty. So i also need to display nothing if the url is empty (""). At the moment it fires hundred of backend calls for 2 objects with the entry.file property :(

I am not that good with Observables and I hope someone can help me with that. Thank you so far :)

3
  • What issue are you facing in the code above? Commented Apr 27, 2020 at 7:14
  • I highly recommend to map the file object beforehand with switchMap to the url. Commented Apr 27, 2020 at 7:44
  • It produces hundred of Backend Calls per Minute. But it should only call once per URL. Commented Apr 27, 2020 at 7:44

1 Answer 1

1

You need to return Observable directly from your method and map your period.claims into one Observable:

// add proper type
entries: Observable<...> = getEntries();

getEntries() {
  // we map every claim to Observable returned from getFileURL method
  const entries = period.claims.map(entry => 
    getFileURL(...).pipe(
      // we use map to return whole entry from Observable - not only url
      map(url => ({
        ...entry,
        url,
      }))
  ));
  // forkJoin will return one Observable with value array when each Observable is completed
  return forkJoin(...entries);
}

public getFileURL(archiveID: string, documentID: string, sysID: string): Observable<...> {
    const request: FileRequest = {
      archiveID: archiveID,
      documentID: documentID,
      sysID: sysID
    };
    return this.fileService.file(request).pipe(filter(Boolean));
}

If you want not to pass to template empty response you could use filter operator and pass Boolean as callback. It will return only truthy values. You can read more about it: https://www.learnrxjs.io/learn-rxjs/operators/filtering/filter You can read also more about forkJoin: https://www.learnrxjs.io/learn-rxjs/operators/combination/forkjoin

Note that adding proper type to method would tell you what you're doing wrong ;)

Sign up to request clarification or add additional context in comments.

10 Comments

This also calls the Backend hundred of times. Why do you use the Observable<File> ? The return of fileService.file() is {url: "url"}
Also I dont understand the 'as file' property. Does it mean i can use the return value of the ngIf? like: when getFileURL() returns a value can i use this return as file then? (e.g. file.url)
Add property fileUrl and use it in template to avoid multiple method invoking - I've edited answer. Type File could be anything. It's just an example.
I would do that, but i could have multiple fileURL. If I had just one, i could use it with fileUrl = getFileURL(...); and then this.fileURL. Do I need to build an array to keep more fileURL?
You mean API could return multiple urls? Or you mean that you need url for every entry? I would map period.claims and make it one Observable.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.