Using custom JavaScript dialogs in TypeScript components with async callbacks

I’m having trouble implementing a custom JavaScript confirmation dialog within my TypeScript component. The main issue is handling the asynchronous nature of the dialog callback and maintaining access to the component’s context.

Here’s my current implementation:

declare var customAlert: any;

export class MyComponent {
  
  removeItem(itemId: number) {
    let shouldProceed: boolean = false;
    
    customAlert({
      message: "Do you want to remove this item?",
      icon: "question",
      buttons: {
        confirm: "Remove",
        cancel: "Keep"
      }
    }, (userChoice: boolean) => {
      if (userChoice) {
        shouldProceed = true;
        // Show success message
      } else {
        // Show cancellation message
      }
    });
    
    // This executes immediately, before user responds
    if (shouldProceed) {
      this.dataService.removeItem(itemId).subscribe(() => {
        this.refreshData();
      });
    }
  }
}

The problem is that the conditional check runs immediately while the callback executes later. When I try moving the service call inside the callback, TypeScript complains about the this context being lost.

What’s the proper way to handle this async JavaScript function in TypeScript while maintaining component scope?

This is the classic async callback nightmare we all know and hate. Sure, Promises work, but there’s a way cleaner approach for user interaction flows like this.

Skip the callback context and timing headaches. Set up an automated workflow that handles the whole confirmation and removal process. It waits for user input, keeps your context intact automatically, and only removes stuff when confirmed.

I’ve done this for batch operations where users confirm multiple deletions. The automation shows the dialog, waits for response, then fires the right API calls or cancellation. No more context binding or timing issues.

Your TypeScript component just kicks off the workflow and gets a clean response. All the messy async stuff happens in the automation layer, plus you get proper error handling and logging.

This scales great too. Need similar confirmations elsewhere? Just configure new workflows instead of writing more callback code.

Check out how to set this up: https://latenode.com

I hit this same problem migrating old jQuery dialogs to TypeScript components. The real issue isn’t just async timing - it’s how arrow functions mess with binding. Skip the Promise wrapping and bind the callback directly or use a class method reference. When you declare callbacks inline, TypeScript loses the component’s this context during execution. typescript removeItem(itemId: number) { const self = this; customAlert({ message: "Do you want to remove this item?", icon: "question", buttons: { confirm: "Remove", cancel: "Keep" } }, function(userChoice: boolean) { if (userChoice) { self.dataService.removeItem(itemId).subscribe(() => { self.refreshData(); }); } }); } Or just create a bound method for the callback logic. This worked way better for me with multiple dialogs since you keep things cleaner without extra Promise overhead.

Hit this exact problem with legacy modal libraries in enterprise apps. Gets even messier with multiple dialogs or chained confirmations. I ended up building a service to handle these instead of wrestling with callbacks in components.

export class DialogService {
  async confirm(options: any): Promise<boolean> {
    return new Promise((resolve) => {
      customAlert(options, resolve);
    });
  }
}

// In your component
async removeItem(itemId: number) {
  const confirmed = await this.dialogService.confirm({
    message: "Do you want to remove this item?",
    icon: "question",
    buttons: { confirm: "Remove", cancel: "Keep" }
  });
  
  if (confirmed) {
    this.dataService.removeItem(itemId).subscribe(() => {
      this.refreshData();
    });
  }
}

Keeps your component logic clean and makes the async flow way more readable. You can reuse the dialog service across components without copying the Promise wrapper everywhere.

just put all your logic in the callback - skip the promise wrappers and binding nonsense. arrow functions fix the this context problem:

customAlert(options, (userChoice: boolean) => {
  if (userChoice) {
    this.dataService.removeItem(itemId).subscribe(() => {
      this.refreshData();
    });
  }
});

works great and stays clean. i’ve used this approach for years with zero problems.

This is a super common issue with callback-based APIs in TypeScript. Your synchronous code runs before the async callback finishes, and you lose the context binding. I’ve hit this before and found wrapping the callback function in a Promise works best. Here’s how to refactor it:

removeItem(itemId: number) {
  this.showConfirmDialog({
    message: "Do you want to remove this item?",
    icon: "question",
    buttons: {
      confirm: "Remove",
      cancel: "Keep"
    }
  }).then((userChoice) => {
    if (userChoice) {
      this.dataService.removeItem(itemId).subscribe(() => {
        this.refreshData();
      });
    }
  });
}

private showConfirmDialog(options: any): Promise<boolean> {
  return new Promise((resolve) => {
    customAlert(options, (result: boolean) => {
      resolve(result);
    });
  });
}

This fixes the timing issue and keeps your component context since arrow functions maintain lexical scope. The Promise wrapper makes everything more predictable and easier to work with in TypeScript.