Anton Scotte
Anton Scotte

Reputation: 439

Stripe Elements with angular 2

I'm trying to integrate Stripe elements with angular 2 with an element card that accepts credit card information. Please note that, I'm not looking into using stripe checkout as there are several examples on this how to integrate stripe with angular 2.

declare var Stripe:any;

@Component({
  selector: 'app-account',
  templateUrl: './account.component.html',
  styleUrls: ['./account.component.scss']
})
export class AccountComponent implements OnInit {

  constructor(
    private _zone: NgZone
  ) { }

  private cardToken:any;

  ngOnInit() {

    if (typeof window !== 'undefined') {
      this.setUpCard();
    }
  }


  setUpCard() {
    var stripe = Stripe('TEST_API_KEY');
    var elements = stripe.elements();
    var style = {
      base: {
        fontSize: '16px',
        lineHeight: '24px'
      }
    };

    var card = elements.create('card', {style: style});

    card.mount('#card-element');
  }

  getCardData(number, month, year, cvc) {
      this.getCardToken(number, month, year, cvc);
  }

  getCardToken(number, month, year, cvc) {
    var dataObj = {"number": number, "exp_month": month, "exp_year": year, "cvc": cvc};

    Stripe.card.createToken(dataObj,
      (status, response) => { 

        this._zone.run(() => {
          if (status === 200) {
            this.cardToken = response;
            console.log("the card token: ", this.cardToken);
          }
          else {
            console.log("error in getting card data: ", response.error)
          }
        });
      }
    );
  }

HTML

<form role="form" id="payment-form">
  <div id="card-element">
    <!-- a Stripe Element will be inserted here. -->
  </div>
</form>

When my component loads I get this error:

The selector you specified (#card-element) applies to no DOM elements that are currently on the page. Make sure the element exists on the page before calling mount().

How can I access the dom element in angular 2 to work properly with Stripe?

Also, I'm using server side rendering on my angular app if this affects how angular works on the client somehow.

Upvotes: 8

Views: 7005

Answers (2)

M Waqas RC
M Waqas RC

Reputation: 66

index.html

<script src="https://js.stripe.com/v3/"></script>

typings.d.ts

declare var Stripe: any;

StripeElementsComponent.ts

export class StripeElementsComponent implements AfterViewInit, OnDestroy { // import AfterViewInit, OnDestroy
    stripe: any;
    elements: any;
    card: any;
    apiKey = 'pk_test______________________'; //replace with your api key
    cardHandler = this.onChange.bind(this);
    error: string;
    @ViewChild('cardInfo') cardInfo: ElementRef; //import viewChild ElementRef

    constructor(private cdr: ChangeDetectorRef) {} //import ChangeDetectorRef

    ngAfterViewInit() {
        this.stripe = Stripe(this.apiKey);
        this.elements = this.stripe.elements();
        const style = { // input card style optional
            base: {
                fontSize: '16px',
                color: '#32325d',
            },
        };
        this.card = this.elements.create('card', {
            style
        });
        this.card.mount(this.cardInfo.nativeElement);
        this.card.addEventListener('change', this.cardHandler);
    }

    ngOnDestroy() {
        this.card.removeEventListener('change', this.cardHandler);
        this.card.destroy();
    }

    onChange({
        error
    }) {
        if (error) {
            this.error = error.message;
        } else {
            this.error = null;
        }
        this.cdr.detectChanges();
    }

    async onSubmitStripe(form: NgForm) { // import NgForm
        const {
            token,
            error
        } = await this.stripe.createToken(this.card);

        if (error) {
            console.log('Something is wrong:', error);
        } else {
            console.log('Success!', token); //response 
        }
    }
}

StripeElementsComponent.html

<form #checkout="ngForm" (ngSubmit)="onSubmitStripe(checkout)">
    <div class="form-row">
        <label for="card-element">Credit or debit card</label>
        <div id="card-element" #cardInfo></div>
        <div id="card-errors" role="alert" *ngIf="error">{{ error }</div>
    </div>
<button>Submit Payment</button>

StripeElementsComponent.css

.StripeElement {
  transition: box-shadow 150ms ease;
  border: solid 3px #e3e3e3;
  border-radius: 0.5em;
  padding: 1.143em 1em;
}

.StripeElement--focus {
  border-color: #85b7d9;
}

.StripeElement--invalid {
  border-color: #fa755a;
}

.StripeElement--webkit-autofill {
  background-color: #fefde5 !important;
}

Upvotes: 2

giovanni.desiderio
giovanni.desiderio

Reputation: 81

I hope you already found a solution to your issue. But, since there are no answers to your question, I'll try to help.

The error you have suggests that the problem lies within the point in the component lifecycle at which you call setUpCard(). You should call that method after the AfterViewInit event, i.e. when the component view is initialized. So you need to move the code you used in the ngOnInit() to the ngAfterViewInit() hook implementation.

Also look at https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#hooks-purpose-timing

Upvotes: 8

Related Questions