I'm using the angular2-wizard to fill in bank account details. On my second step I ask for account info (number, cvc, expiry date) and these elements are from Stripe.js using the ngx-stripe library to mount my elements. Then when I click to the next step and create my Stripe token createToken() I get this error:
IntegrationError: We could not retrieve data from the specified Element. Please make sure the Element you are attempting to use is still mounted.
My thoughts on why this is happening - Either the Angular2-wizard is removing the DOM elements between steps or I'm not waiting for step 2 (account info) to be rendered before I try and mount the elements. I see these posts that are similar here
- Angular 8: Stripe elements nested inside Nebular Components
- Please make sure the Element you are attempting to use is still mounted
QUESTIONS -
- How can I debug this to see if the elements have been removed or reset or hidden in the DOM, when creating my Stripe token?
- How can I wait or ensure the next step (step 2) has been fully rendered in the DOM before I mount my elements?
WHAT I TRIED -
- I checked the element "this.debitCardNumberElement" to see if it was still mounted by calling "this.debitCardNumberElement_isMounted()" from the Stripe library, right before I call createToken() and it seems like it still has values and is mounted. The variable had values, it did't have a null or undefined value, so I figure it's still mounted, but could be wrong.
- Looked to see if the step library had some emitter to let me know when the second step was finished rendering, but I don't think it does. So maybe there is another way to check?
Here is my example
showAccountFieldsAndMountElements($event) {
// mount the 3 elements
this.stripeService.elements(this.stripeElementsOptions).subscribe(elements => {
this.elements = elements;
// Only mount the elements the first time
if (!this.debitCardNumberElement) {
this.debitCardNumberElement = elements.create('cardNumber', this.debitCardNumberElementOptions);
this.debitCardNumberElement.mount('#floatingDebitCardNumber');
this.debitCardNumberElement.on('change', (event) => {
this.cardNumberErrors = event.error ? event.error.message : null;
this.cardNumberValid = event.complete;
})
}
if (!this.debitCardExpiryElement) {
this.debitCardExpiryElement = elements.create('cardExpiry', this.debitCardExpiryElementOptions);
this.debitCardExpiryElement.mount('#floatingDebitCardExpiry');
this.debitCardExpiryElement.on('change', (event) => {
this.cardExpiryErrors = event.error ? event.error.message : null;
this.cardExpiryValid = event.complete;
})
}
if (!this.debitCardCvcElement) {
this.debitCardCvcElement = elements.create('cardCvc', this.debitCardCvcElementOptions);
this.debitCardCvcElement.mount('#floatingDebitCardCvc');
this.debitCardCvcElement.on('change', (event) => {
this.cardCvcErrors = event.error ? event.error.message : null;
this.cardCvcValid = event.complete;
})
}
});
}
createTokenAndShowFinalStep($event) {
this.stripeService.createToken(this.debitCardNumberElement, data).subscribe((result) => {
this.loadingAddress = false;
if (result.token) {
this.externalToken = result.token.id;
this.initNext();
} else if (result.error) {
// Error creating the token
console.log(result.error);
this.alertService.danger(result.error.message);
}
});
}
<form-wizard>
<wizard-step [title]="'Personal Info'" (onNext)="showAccountFieldsAndMountElements($event)">
<h1>Step1</h1>
</wizard-step>
<wizard-step [title]="'Account Info'" (onNext)="createTokenAndShowFinalStep($event)">
<h1>Step2</h1>
<div class="form-floating mb-3">
<div class="form-control stripe-form-control" id="floatingDebitCardNumber" placeholder="ex. 1111 1111 1111 1111"></div>
<label for="floatingDebitCardNumber">Card Number</label>
<div *ngIf="cardNumberErrors" class="invalid-feedback d-block" role="alert">
{{cardNumberErrors}}
</div>
</div>
<div class="row mb-md-3">
<div class="col-md mb-3 mb-md-0">
<div class="form-floating">
<div class="form-control" id="floatingDebitCardExpiry" placeholder="ex. 11/23"></div>
<label for="floatingDebitCardExpiry">Card Expiration</label>
<ng-container *ngIf="cardExpiryErrors">
<span class="text-danger">{{cardExpiryErrors}}</span>
</ng-container>
</div>
</div>
<div class="col-md">
<div class="form-floating">
<div class="form-control" id="floatingDebitCardCvc" placeholder="ex. 123"></div>
<label for="floatingDebitCardCvc">Card CVC</label>
<ng-container *ngIf="cardCvcErrors">
<span class="text-danger">{{cardCvcErrors}}</span>
</ng-container>
</div>
</div>
</div>
</wizard-step>
</form-wizard>