Generating an Observable that connects with a pre-existing function

Currently, I've been attempting to connect the onCompleteItem array function from the ng2-file-upload package with an RxJS Observable method that can be subscribed to.

The function in question looks like this:

onCompleteItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): any;

..which essentially is similar to:

onCompleteItem(item: any, response: any, status: any, headers: any)

Below are the important sections from MyComponent, where everything is currently functioning smoothly:

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);
  };

}

I made an effort to bind this as an Observable using 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();

Unfortunately, this approach encounters some type and syntax errors. Any suggestions on a better way to rewrite this as a bound Observable function?

Answer №1

bindCallback is used to wrap a function that will be called when the observable is subscribed to, like getJSON(url, callback). It seems you are attempting to utilize it by only passing the callback parameter.

Alternatively, you can explore the fromEventPattern function, which allows you to define how the event handler is registered and unregistered using its two parameters.

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>

Answer №2

Embarking on this journey was quite enlightening.. I trust that this answer will offer valuable insights to fellow users.

Initially, I experimented with a few solutions before settling on the one I eventually implemented. Below is a progression of my ideas:

Initial approach:

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 drawback of the above solution is that bindCallBack only triggers once.

A More Polished Approach:

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 sparked an idea within me.. I pondered over using fromEvent as it can subscribe to jQuery actions and browser events, but requires a custom wrapper. This contemplation guided me towards my final resolution, utilizing fromEventPattern:

Final Solution: (complete 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);

      // considering the usage of a Subject
      this.imagesSubject.next(this.imageArray);
    })
  ).subscribe();
}

ngOnDestroy() {
  this.onDestroySubj.next();
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Lack of code completion in Nuxt options API when using typescript

After setting up Nuxtjs with typescript, I noticed that there are no code completions in the template and script as expected based on the title. Here is the code: <script lang="ts"> import Vue from 'vue'; import { FeaturedJobs } ...

Best practices for working with child components in Vue.js using TypeScript: Steer clear of directly mutating props

I feel like I'm stuck in a loop here. Currently, I have a Vue.js 2 app set up and running with TypeScript. However, I'm encountering an issue when trying to pass data to a child component that originates from the store. <template> < ...

Error: Failed to execute close function in inappbrowser for Ionic application

Working on integrating the "in-app-browser" plugin with my Ionic project. Check out the code snippet below: const browser = this.iab.create(mylink, '_blank'); browser.on('loadstop').subscribe( data => { if ...

What is the rationale behind TypeScript's decision to permit omission of "this" in a method?

The TypeScript code below compiles without errors: class Something { name: string; constructor() { name = "test"; } } Although this code compiles successfully, it mistakenly assumes that the `name` variable exists. However, when co ...

Creating a flexible TypeScript function handler that accepts optional arguments depending on the function's name

I am facing a challenge with defining the function type for running helper functions that prepare database queries. Some of these functions have arguments, while others do not. TS Playground Link type PreparedQuery = { query: string; params?: string[] ...

I'm curious if there is a method to implement Angular Datatables that includes a filter for every column in a more streamlined and efficient manner?

Incorporating Angular 7 and Angular DataTables "the angular way", I aim to implement individual column filters similar to "Individual column searching" as demonstrated here: , which is presented as a non-angular approach. My attempts to merge "the angular ...

Type returned by a React component

I am currently using a basic context provider export function CustomStepsProvider ({ children, ...props }: React.PropsWithChildren<CustomStepsProps>) => { return <Steps.Provider value={props}> {typeof children === 'function&ap ...

if and elsif JSON with Angular

Currently, I am working on completing a task within our project. To provide more context, I have a field with items that I must select. Once I choose an item, another field appears in which I must select additional elements. These elements need to be sen ...

Display an error popup if a server issue occurs

I'm considering implementing an Error modal to be displayed in case of a server error upon submitting a panel. I'm contemplating whether the appropriate approach would be within the catch statement? The basic code snippet I currently have is: u ...

What is the substitute for <center> tag in AngularJS?

Error: Template parse errors: 'center' is not a recognized element: 1. If 'center' is supposed to be an Angular component, make sure it is included in this module. 2. To permit any element, add 'NO_ERRORS_SCHEMA' to the ' ...

I am experiencing an issue with mydaterangepicker and primeng where it is not displaying properly in the table header. Can anyone assist me with this

I am attempting to integrate mydaterangepicker () with primeng turbotable (since primeng calendar does not meet the requirements), but I am having trouble with its display. Could you please assist me with some CSS code or suggest an alternative solution? ...

Solving automatically generated TypeScript MongoDB types for GraphQL results

Utilizing the typescript-mongodb plugin along with graphql-codegen to automatically generate Typescript types enables easy data retrieval from MongoDB and GraphQL output via Node. The initial input schema in GraphQL format appears as follows: type User @ ...

Why does the custom method only trigger once with the addEventListener?

I am attempting to connect the "oninput" event of an input range element to a custom method defined in a corresponding typescript file. Here is the HTML element: <input type="range" id='motivation-grade' value="3" min="1" max="5"> This i ...

Automatically convert user input to MM/DD/YYYY format in typescript as they type the date

Hello, I am currently working on a React Native app where users input their date using TextInput. As the user types, I would like to automatically format the date as MM/DD/YYYY. Here is the function I have created so far: const formatDate(value: string ...

POSTMAN - error when making a post request with the message "The media type 'application/json' is not compatible with this resource."

Snippet of Web API code: [HttpPost] [ODataRoute("GetCMMAutoForLoggedInUser")] public IHttpActionResult GetCMMAutoForLoggedInUser(CMMPermissionRequest request) { var data = this.CommonDomain.GetCMMAutoForLoggedInUser(request); return Ok(data); } ...

The metadata for [object Module] does not contain any ngModule information

I recently made changes to my routes by switching them from lazy loaded using a string reference to lazy loading through a call to import. However, I am facing an issue where every time I try to navigate to one of the pages, I encounter the following erro ...

Typescript does not allow for extending an interface with a data property even if both interfaces have the same data type

I've encountered a peculiar problem with Typescript (using Visual Studio 2012 and TypeScript v0.9.5) that I could use some help clarifying. The code snippet below functions correctly: interface IA { data: any; } interface IB { data: any; } ...

Creating mock objects with Jest

I am currently delving into the world of jest testing. Here is a snippet from an implementation class I'm working with: import { ExternalObject } from 'external-library'; export class MyClass { public createInstance(settings : ISettings) ...

Leveraging Angular environment configurations for workspace libraries

I've been experimenting with Angular's latest version (>6) and am delving into the concept of using workspaces. One interesting feature is the ability to generate a library project with a simple CLI command: ng generate library api In my de ...

What is the best way to utilize the async pipe along with @defer for efficiently loading and declaring variables in the template within Angular 17

One way I can accomplish this is by using @if. An example of this is: @if(items$ | async; as items), where I can assign the array of items to a variable named 'items' using the 'as' keyword in the template. Is there a similar approach ...