Reputation: 439
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
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
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