Having trouble accessing an injector service within the promise of a dynamically loaded JavaScript function that has been assigned to a global variable

Query

I am facing an issue while trying to integrate PayPal with Angular. I am encountering difficulties when attempting to call an injected service inside a function of the promise returned. Any assistance in resolving this would be greatly appreciated.

Here are the steps I am following:

  1. Click on the load PayPal button.
  2. Load the dynamically injected Braintree and PayPal sdk.
  3. Authorize into PayPal and get the nonce token.
  4. Pass the generated nonce token to the injected service within the component.

Code snippet below

app.component.html


<button (click)="initializePayPal()">load paypal</button>
<div id="pay-pal-btn"></div>

app.component.ts

export class AppComponent {
  constructor(private backEndService: BackEndService) {}
  initializePayPal() {
    get("https://js.braintreegateway.com/web/3.71.1/js/client.min.js", () => {
      get(
        "https://js.braintreegateway.com/web/3.71.1/js/paypal-checkout.min.js",
        () => {
          console.log("loaded");
          this.loadPayPalSDK();
        }
      );
    });
  }

  private loadPayPalSDK(): void {
    braintree.client
      .create({
        authorization: "sandbox_5rnr7xqg_kx8tzdyvfcrnxq5y"
      })
      .then(clientInstance => {
        // Create a PayPal Checkout component.
        return braintree.paypalCheckout.create({
          client: clientInstance
        });
      })
      .then(paypalCheckoutInstance => {
        return paypalCheckoutInstance.loadPayPalSDK({
          vault: true
        });
      })
      .then(paypalCheckoutInstance => {
        return paypal
          .Buttons({
            locale: "en_US",
            fundingSource: paypal.FUNDING.PAYPAL,
            style: {
              height: 40
            },
            createBillingAgreement() {
              return paypalCheckoutInstance.createPayment({
                flow: "vault"
              });
            },
            onApprove(data, actions) {
              return paypalCheckoutInstance
                .tokenizePayment(data)
                .then(payload => {
                  console.log(payload.nonce);
                  this.backEndService.addToken(payload);
                });
            },

            onCancel(data) {},

            onError(err) {
              //
            }
          })
          .render("#pay-pal-btn");
      });
  }
}

Back End Service backend.service.ts

import { Injectable } from "@angular/core";

@Injectable({ providedIn: "root" })
export class BackEndService {
  addToken(token: string) {
    // here is am going to use the token generated and pass this value to backend service using http request.
    console.log("token" + token);
  }
}

Expected Solution

I need help implementing a solution where the injected service is called upon invoking the tokenizePayment method.

Thank you in advance for your help.

StackBlitz URL

https://stackblitz.com/edit/kp-angular-paypal?file=src/app/app.component.ts

Answer №1

The issue at hand is directly associated with the scope you are working in.

// The problem arises from losing scope when calling paypal.Buttons
// This function operates within its own scope, making it impossible to access 
// the backend service which has a separate scope (this)
return paypal.Buttons({
           onApprove() {
              return paypalCheckoutInstance
                .tokenizePayment(data)
                .then(payload => {
                  this.backendservice // <- Undefined due to scope issues
                });
             }
           })

Fortunately, there are two solutions available to tackle this scenario.

  1. Utilize a static method.
        
return paypal.Buttons({
            onApprove() {
              return paypalCheckoutInstance
                .tokenizePayment(data)
                .then(payload => {
                  BackEndService.sendToken(payload.nonce);
                });
             }
           })

backend.service.ts

export class BackEndService {
  // Static method implementation
  static sendToken(token: string) {
        console.log("[backend service] send token", token);
  }

  1. Indirectly pass our scope using an object containing a reference to our service.
  // Create an object that calls our service within our scope
  // This object needs to be passed to our 
  // loadPayPalSDK method
  config = {
    onApprove: (token: string) => {
      this.backEndService.addToken(token);
    }
  };


// Implementing the config here
private loadPayPalSDK(config = this.config): void {
 // Code omitted ...
 return paypal.Buttons({
            onApprove() {
              return paypalCheckoutInstance
                .tokenizePayment(data)
                .then(payload => {
                  config.onApprove(payload.nonce);
                });
             }
           })

}

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

What is the best way to ensure a texture is always facing the camera?

Latest Update: We have created a new demo to showcase the desired outcome. The demo includes an invisible skydome, a cubecamera, and an environment map. However, it is important to note that these techniques should not be utilized for the reasons previous ...

What is the process for enabling multiple consumers to subscribe to a shared topic in RabbitMQ and receive identical messages?

Although there is a similar question with an answer here, I remain uncertain whether the limitation lies in RabbitMQ's capabilities or if I simply need to conduct further research. Coming from a JS/Node background where the event pub/sub pattern func ...

What is the best approach to creating DOM elements in AngularJS?

My controller contains data with various actions and different numbers of parameters: $scope.actions = [ {name : 'rotate', r : '30'}, {name : 'translate', x : '10', y : '10'}, {name : 'scale', ...

The Best Way to Display Quad Wireframe in Three.js using OBJ Model

I am trying to display the wireframe of OBJ meshes using Three.js. However, Three.js can only render triangles, so I came up with the idea of creating a line for each edge of the model. After parsing the OBJ file and iterating through its vertices and fac ...

Create a TypeScript interface that represents an object type

I have a Data Structure, and I am looking to create an interface for it. This is how it looks: const TransitReport: { title: string; client: string; data: { overdueReviews: number; outstandingCovenantBreaches ...

What is the best way to incorporate the final paragraph into the foundational code?

I'm attempting to create my own version of the lyrics for 99 Bottles of Beer Here is how I've modified the last line: 1 bottle of beer on the wall, 1 bottle of beer. Take it down and pass it around, no more bottle of beer on the wall. How ...

Discovering old (potentially neglected) npm dependencies: strategies for locating outdated packages

I am aware of the npm outdated command, which shows out-of-date dependencies only if a new version has been published. However, in certain cases (such as abandoned projects), there may not be a new version released and the package could still be considere ...

Guide on how to compare two arrays in JavaScript and identify mismatches by their respective indices

let x=["e","f","g","h"]; let y=["f","e","g","h"]; I want the following result: Inconsistent array from x Inconsistency array=["e", "f"]; ...

Issues with expanding all nodes in the Angular Treeview function by Nick Perkins in London are causing difficulties

Currently utilizing the angular treeview project found here: https://github.com/nickperkinslondon/angular-bootstrap-nav-tree After examining the functionality, it seems that this treeview is lacking search capabilities. To address this limitation, I deci ...

Using Angular to display asynchronous data with ngIf and observables

In cases where the data is not ready, I prefer to display a loader without sending multiple requests. To achieve this, I utilize the as operator for request reuse. <div class="loading-overlay" *ngIf="this.indicatorService.loadingIndicators[this?.indic ...

Transmit an audio buffer to the client for direct download without the need for server storage

As part of my project, I am developing a text-to-speech feature utilizing the technology of IBM Watson API. With the assistance of the code snippet below, I have successfully managed to acquire the .wav file after conversion onto my server. textToSpeech ...

observe the file currently residing on the server

Is there a way to display a server-based file within an HTML page using Angular 8.0.0? I attempted to use ngx-file-viewer, but encountered the error "Property 'wheelDelta' does not exist on type 'WheelEvent'". To address this issue, I ...

Is there a way to prevent the imported JQuery from causing issues with current code implementations?

Being a novice developer in Html/Javascript/CSS/Jquery coding, I recently encountered an issue while integrating Jquery into my project. When I imported Jquery, the styling of simple buttons went haywire. Jquery worked perfectly fine when using its classes ...

Exploring SVG Morphing Reversal Techniques in Anime.js

I have been trying to implement direction: 'reverse' and timeline.reverse(), but so far it hasn't been successful. Interestingly, when I set loop: true, the reverse animation can be seen within the loop. However, my goal is to trigger this a ...

Contrasting deleting a node_module folder with running npm uninstall to remove a specific package

Do manual deletion of a package directly from the node_modules folder and running npm uninstall really make any difference, considering that npm just deletes the package anyway? ...

Adjust the marginLeft and marginRight values in a JavaScript statement within a Highcharts configuration

Is there a way to adjust the chart marginLeft and marginRight using Highcharts, and then redraw it in JavaScript? I am looking to change the chart margin dynamically at different points in my code. http://jsfiddle.net/ovh9dwqc/ I attempted to do this wit ...

Error: Observable<any> cannot be converted to type Observable<number> due to a piping issue

What causes the type error to be thrown when using interval(500) in the code snippet below? const source = timer(0, 5000); const example = source.pipe(switchMap(() => interval(500))); const subscribe = example.subscribe(val => console.log(val)); V ...

Guide to creating a generic that captures the prop types of a given component

Is there a way to create a function that accepts a component and uses its prop type as the type of the second parameter? For example, if I provide a component with the type React.FunctionComponent<IMovieShowcase> How would I go about extracting the ...

Request with an HTTP header for authentication

Here's a straightforward question that may seem simple to some developers but could be tricky for beginners. So, my question is - how can I send an HTTP request from a browser when a user clicks a button and also add an authorization header to the re ...

Persistent error caused by unresponsive Tailwind utility functions

Currently, I am working on a Next.js application and encountered a strange error while adjusting the styling. The error message points to a module that seems to be missing... User Import trace for requested module: ./src/app/globals.css GET /portraits 500 ...