我有一个带有贝宝按钮的页面,但有一半时间无法加载。我刚刚连续刷新了30次页面。连续交替工作9次,然后连续11次不加载,然后连续10次加载。我完全迷失了。测试中发生的情况与实际发生的情况相同。
按钮未加载时的错误
deposittest.component.ts:224 TypeError: Cannot read properties of undefined (reading 'nativeElement')
at deposittest.component.ts:218:36
at _ZoneDelegate.invoke (zone.js:368:26)
at Object.onInvoke (core.mjs:26321:33)
at _ZoneDelegate.invoke (zone.js:367:52)
at Zone.run (zone.js:129:43)
at zone.js:1257:36
at _ZoneDelegate.invokeTask (zone.js:402:31)
at core.mjs:25998:55
at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:25998:36)
at _ZoneDelegate.invokeTask (zone.js:401:60)
删除了一些代码的 ts 文件
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { loadScript, PayPalNamespace } from "@paypal/paypal-js";
@Component({
selector: 'app-deposittest',
templateUrl: './deposittest.component.html',
styleUrls: ['./deposittest.component.css']
})
export class DeposittestComponent implements OnInit {
depositState$: Observable<State<CustomHttpResponse<User>>>;
private dataSubject = new BehaviorSubject<CustomHttpResponse<User>>(null);
private isLoadingSubject = new BehaviorSubject<boolean>(false);
isLoading$ = this.isLoadingSubject.asObservable();
DataState = DataState;
private isLoggedInSubject = new BehaviorSubject<boolean>(false);
isLoggedIn$ = this.isLoggedInSubject.asObservable();
PAYPAL_CLIENT_ID: string = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
myAmount: any
@ViewChild('paypalRef', { static: false }) private paypalRef!: ElementRef;
constructor(private notificationService: NotificationService, private router: Router, private userService: UserService) {
}
ngOnInit(): void {
this.depositState$ = this.userService.depositInit$()
.pipe(
map(response => {
console.log(response);
this.dataSubject.next(response);
return {
dataState: DataState.LOADED, appData: response
};
}),
startWith({ dataState: DataState.LOADING }),
catchError((error: string) => {
return of({ dataState: DataState.ERROR, error })
})
)
this.renderPaypalButton();
}
renderPaypalButton() {
let orderRef: string | null = null;
loadScript({
"clientId": this.PAYPAL_CLIENT_ID,
currency: 'USD',
disableFunding: 'paylater,credit,card,venmo',
})
.then((loadedPaypal) => {
if (loadedPaypal) {
const paypal: PayPalNamespace = loadedPaypal;
paypal.Buttons({
style: {
layout: 'vertical',
color: 'blue',
shape: 'rect',
label: 'paypal',
},
createOrder: (data, actions) => {
const postData = {
amount: this.myAmount,
state: this.state
};
return fetch(AppSettings.API_ENDPOINT + '/user/create_paypal_transaction', {
method: "post",
headers: {
'Authorization': 'Bearer ' + localStorage.getItem(Key.TOKEN),
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
})
.then((response) => response.json())
.then((response) => {
// console.log("create order response ");
// console.log(response);
// console.log("create order id ");
// console.log(response.data.order.id);
return response.data.order.id;
});
},
onApprove: (data, actions) => {
return fetch(AppSettings.API_ENDPOINT + `/user/paypal/capture_order/${data.orderID}`, {
method: "post",
headers: {
'Authorization': 'Bearer ' + localStorage.getItem(Key.TOKEN),
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((response) => {
this.paypalRedirect(response.data.order.status, orderRef);
});
},
onCancel: () => {
//
},
onError: (err) => {
this.notificationService.onError('Error processing deposit.');
this.paypalRedirect('ERROR', orderRef);
}
}).render(this.paypalRef.nativeElement);
} else {
console.error("");
}
})
.catch((error) => {
console.error("", error);
});
}
paypalRedirect(status: string, orderRef: string | null) {
switch (status) {
case 'COMPLETED':
if (orderRef) {
this.notificationService.onDefault('Payment Successful.');
this.router.navigate(['/paypal/result/success/']);
} else {
console.error("Missing success redirect");
}
break;
case 'ERROR':
if (orderRef) {
this.notificationService.onError('Payment could not be completed.');
this.router.navigate(['/']);
} else {
console.error("Missing failure redircte");
}
break;
default:
console.error("redirection.");
break;
}
}
}
html 文件
<ng-container *ngIf="(depositState$ | async) as state" [ngSwitch]="state.dataState">
<ng-container *ngSwitchCase="DataState.LOADED">
<app-navbar [user]="state?.appData?.data?.user"></app-navbar>
<section>
<div id="payment" class="mt-3">
<div class="col-md-12">
<div class="form-group">
<div style="font-weight: bold;">Name on file: {{ state?.appData?.data?.user.firstName+'
'+state?.appData?.data?.user.lastName}} </div>
<div class="mt-2" style="font-style: italic;font-size: 1rem;">Your name on file must match the
name associated with your form of payment.</div>
</div>
</div>
<div id="input" class="form-group mt-3">
<label style="font-size: .8rem;" class="me-2">Enter Amount: $10 - $999</label>
<input type="text" (keypress)="decimalFilter($event)" placeholder="Enter amount here" ngModel
name="myAmount" (ngModelChange)="myAmount=$event" value="{{myAmount }}"
class="form-control mb-2">
</div>
<div #paypalRef></div>
</div>
</section>
</ng-container>
<ng-container *ngSwitchCase="DataState.LOADING">
<div>Loading...</div>
</ng-container>
<ng-container *ngSwitchCase="DataState.ERROR">
<div>{{ state.error }}</div>
</ng-container>
<app-authfooter></app-authfooter>
</ng-container>
如有任何建议,我将不胜感激。谢谢你
您遇到了竞争条件,您等待
depositState$
加载并渲染页面。同时调用 renderPaypalButton()
方法。如果加载状态足够快,贝宝按钮将呈现得很好。如果不是,则在您尝试渲染按钮时模板不可用。
所以你应该做的是引入一个
tap
函数,在其中检查状态是否已定义,因此你的模板已准备好并将 renderPaypalButton()
放入其中。
this.userService.depositInit$()
.pipe(
// ...other code
tap((pageLoaded) => {
if (pageLoaded) this.renderPaypalButtons()
})
)
这也是为什么将
renderPaypalButtons()
放在 ngAfterViewInit 中不会产生任何影响,因为整个页面取决于第一个 *ngIf。