Bux
Bux

Reputation: 57

Angular 6 : external js library and binding problem

In my APP I've loaded an external library "braintree.js"... everything is correctly working, but there's a strange behaviour that I can't understand.

In my template a block of divs are loaded depending on the value of a var (regStep): when I call the external library, even if the value of regStep changes, the template doesn't show the changes, If I comment the call to the external library and change the value of regStep, template changes accordingly.

I haven't copied all the code,but I assure that all the needed variables are declared.

I'm pretty new to Angular,maybe I'm losing something, but I really do not understand where's the problem.

These are the involved files:

component.html

<div *ngIf="regservice.regStep == 1">
   <form (ngSubmit)="regservice.savePay();
   ...tags...
   </form>
</div>
<div *ngIf="regservice.regStep == 2">
   ...tags...
</div>

regservice.ts

declare var braintree: any;

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';

@Injectable({
   providedIn: 'root'
})

export class RegisterService {

  constructor(public router: Router, private http: HttpClient) {}
  public regStep = 1;

public savePay() : void {

let self = this;

let client = new braintree.api.Client({clientToken: this.bt_token});

client.tokenizeCard({
  number: '',
  expirationDate: '',
  cvv : ''
}, function (err, nonce) {
  if (nonce) {
    console.log(nonce); 
    self.addPayment(nonce);
  }
});
//this.addPayment('fake-valid-nonce');
//If I uncomment the above line and comment all the others everything works perfectly
}

private addPayment(nonce) : void {  

 this.http.post(this.apiUrl+'/bt-add-payment-method/',
{
    payment_method_nonce: nonce,
    billing_address_id: this.business.braintree_customer.id,
    make_default: true

},
{
  headers:{
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + this.token['access_token']
  }}).subscribe( {
      next : response => { console.log('RESPONSE ADD PYMNT : ',response); },
      error: err => {this.handleErrors(err); },
      complete: () => { this.forward(); },
    }); 
 }

 private forward() {
  switch (this.regStep) {
     case 1 : {
        this.regStep_value = 'Step 2 of 5';
        this.regStep++;             
    }
    break;
  }
}

UPDATE

Even using :

client.tokenizeCard({
  number: '',
  expirationDate: '',
  cvv : ''
}, (err, nonce) => {
    console.log(nonce); 
    this.addPayment(nonce);  
});

Problem persists. Template remains blocked to STEP == 1 block.

If I do not call new braintree and related code, everything works.

In both cases I can see the console.log RESPONSE ADD PYMNT, so AddPayment function is in both cases called; also forward function is in both cases called.

In both cases I can see that regStep is 2, but

  1. if i called braintree, template remains blocked to the previous state.

  2. if i didn't call braintree, template changes as soon as regStep changes.

Upvotes: 1

Views: 275

Answers (2)

Bux
Bux

Reputation: 57

Problem was related to an asynchronous function exiting the Angular zone.

This will resolve the issue:

client.tokenizeCard({
  number: '',
  expirationDate: '',
  cvv : ''
}, (err, nonce) => {
  this.zone.run(() => { //<=== added
    console.log(nonce); 
    this.addPayment(nonce);  
  })
});

logically you have to import NgZone

import { NgZone } from '@angular/core';

export class RegisterService {

  constructor(private zone:NgZone ) {}
}

Upvotes: 1

alokstar
alokstar

Reputation: 499

If you want to use the component variables/methods inside the callback, a better way is using a lambda (=>) function instead of using literal or anonymous functions. In your case, you may use the following:

(err, nonce) => {   
   if (nonce) {
       console.log(nonce); 
       this.addPayment(nonce);   
    }
}

Upvotes: 0

Related Questions