matus moravcik
matus moravcik

Reputation: 151

Angular 5 callback and that=this

in my Angular 5 project I have some problems with the callback from an external library. I load it using

<script src="https://js.chargebee.com/v2/chargebee.js" data-cb-site="my-site"> </script>

and then in Angular I import it as follows:

declare var Chargebee: any;
Chargebee.init({site: "my-site"});

I also have a public variable in my component, lets say publicVar, which I am displaying in the template.

publicVar: string = 'before checkout';

I have the following method:

subscribeToPlan() {
  var _self = this;
  var chargebeeInstance = Chargebee.getInstance();

  chargebeeInstance.openCheckout({
    hostedPage: function() {
      _self.publicVar = 'hosted-page';
      console.log('hosted-page');
    },
    success: function(hostedPageId) {
      _self.publicVar = 'success';
      console.log('success');
    },
    close: function() {
      _self.publicVar = 'closed';
      console.log('closed');
    }
  });
}

What is happening when I run the code?

All the console.log functions output the correct data, so I know the chargebee callbacks are called. However, only hostedPage: function() {} correctly changes my publicVar, and it says "hosted-page" in my template.

success: function(){} nor close: function(){} won't update the publicVar in my template. I suspect because these are, unlike the hostedPage, a callback methods and the self. in them has wrong context?

Upvotes: 3

Views: 3133

Answers (2)

matus moravcik
matus moravcik

Reputation: 151

So I worked it out (or found a workaround)

As the _self did hold the correct data, I thought that the change detection is not triggered for those callbacks. After I've manually triggered it, it all works as expected.

Final code and changes:

Import

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

Add it to the constructor:

constructor(private cd: ChangeDetectorRef)

And then call it at the end of the callback method, this will manually trigger Angular change detection, and updates template rendering of publicVar

subscribeToPlan() {
  var _self = this;
  var chargebeeInstance = Chargebee.getInstance();

  chargebeeInstance.openCheckout({
    hostedPage: function() {
      _self.publicVar = 'hosted-page';  // This is not a callback, so it just works
      console.log('hosted-page');
    },
    success: function(hostedPageId) {
      console.log('success');
      _self.publicVar = 'success';
      _self.cd.detectChanges();  // Manual change detection
    },
    close: function() {
      console.log('closed');
      _self.publicVar = 'closed';
      _self.cd.detectChanges();  // Manual change detection
    }
  });
}

Alternative code using the arrow functions, as per Joe's suggestion (https://stackoverflow.com/a/50335020/5644425)

subscribeToPlan() {
  var chargebeeInstance = Chargebee.getInstance();

  chargebeeInstance.openCheckout({
    hostedPage: () => {
      this.publicVar = 'hosted-page';
      console.log('hosted-page');
    },
    success: hostedPageId => {
      console.log('success');
      this.publicVar = 'success';
      this.cd.detectChanges();
    },
    close: () => {
      console.log('closed');
      this.publicVar = 'closed';
      this.cd.detectChanges();
    }
  });
}

Upvotes: 2

Joe
Joe

Reputation: 7004

Rather than assigning this to some variable _self you could just use arrow functions, which don't affect the scope of this:

subscribeToPlan() {
  var chargebeeInstance = Chargebee.getInstance();

  chargebeeInstance.openCheckout({
    hostedPage: () => {
      this.publicVar = 'hosted-page';
      console.log('hosted-page');
    },
    success: hostedPageId => {
      this.publicVar = 'success';
      console.log('success');
    },
    close: () => {
      this.publicVar = 'closed';
      console.log('closed');
    }
  });
}

And without the logging this becomes the even neater:

subscribeToPlan() {
  var chargebeeInstance = Chargebee.getInstance();

  chargebeeInstance.openCheckout({
    hostedPage: () => this.publicVar = 'hosted-page',
    success: hostedPageId => this.publicVar = 'success',
    close: () => this.publicVar = 'closed'
  });
}

Though I think your code should work fine. You're assigning this to _self and using that, so the this of the functions shouldn't matter. I'd definitely recommend putting in a breakpoint and checking what _self is in those two functions.

Upvotes: 1

Related Questions