Search code examples
angularpromiseobservablesubscription

How to return subscription and promise from single function in angular?


I am working on one angular project in which I have file upload feature is implemented. I have created one upload service and make one function inside it for upload file

service.ts :

upload(file: File){
  return new Promise(resolve=>{
    var file = file;
    bucket.upload(file).on("httpUplaodprogress", (progress)=>{
        const percent = progress.percent

        -->  Here I want to emit this percent value from this function. and want to get it where it is from called.


    }).send(response =>{
      response.error ? reject(response.err);
      resolve(response);
    })
  })
}

Now this upload service is used at many place.

E.g :

uploadComponent.ts :

progress:number = 0;

constructor(private uploadService: UploadService) {}

handleFileInput(file){
    this.uploadService.upload(file).then(result=>{
       result ? show success msg for file uplaod success : show error

       ==> I want to subscribe service's upload function's progress variable 
           here for assign that value into this host component's progress variable which is 
           bind with progressbar copmponent.
   
    })
}

uploadComponent.html

<progressbar [progress]="progress" />

This is current scenario where we can't access progress variable from service function into host component where service function is called.

one way is that, we can get subscribe service function into host component and can progress value can continuously get. but how ?

because service function already return promise. how can I use subscription inside promise and subscribe it into host component.

I had also thought about global subscription variable into service and subscribe it into host component. but it can cause problem when upload file from multiple places and show progressbar.

I want to keep subscription variable's scope local for each call.

If anyone know about it, how to broadcast value from promise function and receive at host function from where it is called.. then please reply this. It's very helpful for me.


Solution

  • Your should return an Observable from your service instead of a Promise.

    UploadService

    // note that BucketUploadResponse is an interface I made up
    upload(file: File): Promise<BucketUploadResponse> {
         // delegate to our Observable implementation and convert to promise
         return this.upload$(file).toPromise();
    }
    
    // note that BucketUploadResponse is an interface I made up
    upload$(file: File): Observable<number | BucketUploadResponse> {
            return new Observable<number>((subscriber) => {
                bucket
                    .upload(file)
                    .on('httpUplaodprogress', (progress) => {
                        const percent = progress.percent;
                        // emit progress
                        subscriber.next(percent);
                    })
                    .send((response) => {
                        if (response.error) {
                            // error out
                            subscriber.error(response.error);
                        } else {
                            // emit response as last item to keep old contract
                            subscriber.next(response);
                            subscriber.complete();
                        }
                    });
    
                return () => {
                    // called when no subscription is left. You could abort the upload here for example
                };
            });
        }
    

    In the consuming component, you can subscribe to the Observable:

        progress:number = 0;
        
        private uploadSubscription?: Subscription;
    
        constructor(private uploadService: UploadService) {}
    
        handleFileInput(file){
            this.progress = 0;
            // unsubscribe from an existing upload
            this.uploadSubscription?.unsubscribe();
            this.uploadSubscription = this.uploadService.upload(file).subscribe((progress) => {
                if (typeof progress === 'number') {
                    this.progress = progress;
                } else {
                    // handle BucketUploadResponse if required
                }
            }, () => {
                // complete, show success message
            }, (error) => {
                // error, show error message
            })
        }