Reputation: 2991
I've been trying to bind the onCompleteItem
array function from the ng2-file-upload
package to an RxJS Observable method that I can subscribe to.
Here is function I'm talking about:
onCompleteItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): any;
..which in use is almost the same as:
onCompleteItem(item: any, response: any, status: any, headers: any)
Here's the relevant parts from MyComponent, which is currently working:
imageArray: Image[] = [];
uploader:FileUploader = new FileUploader({
url: '/api/FileUpload'
});
ngOnInit() {
this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
const data = JSON.parse(response);
data.forEach(x => {
console.log(x);
this.imageArray.push(x);
}
this.uploader.removeFromQueue(item);
};
}
My attempt at binding this into an Observable was through the use of the RxJS bindCallback
function:
bindCallback(this.uploader.onCompleteItem, {item: any, response: any, status: any, headers: any})().pipe(
tap((item, response) => {
const data = JSON.parse(response);
this.uploader.removeFromQueue(item);
const images = data.map(x => console.log(x) );
this.imageArray.push(...images);
})
).subscribe();
This does not work, because of a few type/syntax errors. Does anyone have any good suggestions on how to get this rewritten as a bound Observable function?
Upvotes: 0
Views: 209
Reputation: 2991
This was a real journey.. But I hope this answer gives some users new insight.
Here's a few solutions that worked that I went from initially, with the last solution listed being what I ended up implementing.
Initial solution:
const obs = new Observable<{ item: FileItem, response: string }>(sub => {
this.uploader.onCompleteItem = (item: FileItem, response: string) => sub.next({ item, response });
return () => this.uploader.onCompleteItem = undefined;
});
bindCallback((x: (item: FileItem, response: string) => any) => this.uploader.onCompleteItem = x)()
.pipe(
takeUntil(this.onDestroySubj),
tap(([item, response]) => {
const data = JSON.parse(response);
this.imageArray.push(...data );
this.uploader.removeFromQueue(item);
})
).subscribe();
The only problem with the solution above is that bindCallBack
only fires once.
A More Refined Solution:
Observable.create((sub: Subscriber<[FileItem, string]>) => {
this.uploader.onCompleteItem = (item: FileItem, response: string) => sub.next([item, response]);
return () => this.uploader.onCompleteItem = undefined;
}).pipe(
takeUntil(this.onDestroySubj),
tap(([item, response]) => {
const data = JSON.parse(response);
this.imageArray.push(...data );
this.uploader.removeFromQueue(item);
})
).subscribe();
This got me thinking.. And I considered using fromEvent
because it subscribes to jQuery actions and browser events, but not unless a wrapper is written. Which is what led me to my final solution, using fromEventPattern
:
Final Solution: (full code)
ngOnInit() {
fromEventPattern<Parameters<FileUploader['onCompleteItem']>>(x => this.uploader.onCompleteItem = x).pipe(
takeUntil(this.onDestroySubj),
tap(([item, response]) => {
this.imageArray = this.imagesSubject.getValue();
const data = JSON.parse(response);
this.imageArray.push(...data );
this.uploader.removeFromQueue(item);
// because I'm using a Subject
this.imagesSubject.next(this.imageArray);
})
).subscribe();
}
ngOnDestroy() {
this.onDestroySubj.next();
}
Upvotes: 0
Reputation: 1960
bindCallback
wraps a function that's called whenever the observable is subscribed to, e.g. getJSON(url, callback)
. You're trying to use it just by passing the callback
parameter.
Instead you can try the fromEventPattern
function, which allows you to specify via its two parameters how you register the event handler, and also unregister.
const {fromEventPattern} = rxjs;
const uploader = {
upload() {
setTimeout(() => {
if (this.onCompleteItem) this.onCompleteItem('arg 1', 'arg 2');
}, 1000);
}
};
const source = fromEventPattern(handler => uploader.onCompleteItem = handler, _ => uploader.onCompleteItem = null);
source.subscribe(([arg1, arg2]) => console.log(arg1, arg2));
uploader.upload();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.4/rxjs.umd.js"></script>
Upvotes: 1