Home > Software engineering >  Page crash after converting to Angular Universal (SSR)
Page crash after converting to Angular Universal (SSR)

Time:09-17

First, apologies for the vague title, I can't make it more specific without going super long. So I have this confirmation page after the user make payment on a 3rd party payment gateway (think PayPal), it bounce back to my site with query params. Then I present the page with all the info (payment method x is success etc etc). That page is working fine with non-SSR site.

The relevant codes:

@Component({
  selector: 'app-payment-result',
  templateUrl: './payment-result.component.html',
  styleUrls: ['./payment-result.component.css']
})
export class PaymentResultComponent implements OnInit {
  isBusy: boolean;
  data: PaymentResult;
  success: boolean;
  isPaymentInformationBusy = true;
  paymentInformation: PaymentInformationResult[];

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private requestService: RequestService,
    private modalService: ModalService
  ) {
    this.route.queryParams.subscribe(params => {
      console.log(params)
      if (params) {
        this.getData(params?.merchantOrderId);
        this.success = params?.resultCode === '00';
      } else {
        this.router.navigate(['/']);
      }
    });
  }

  ngOnInit(): void { }

  getData(transactionNo: string): void {
    this.isBusy = true;
    this.requestService.get<PurchaseTransactionResult>(
      'purchase-transaction',
      obj => {
        this.data = obj;
        this.getPaymentInformation(obj?.PaymentMethodInformation?.ID);
      },
      failed => {
        console.log(failed);
      },
      error => {
        console.log(error);
      },
      () => {
        this.isBusy = false;
      },
      {TransactionNo: decodeURIComponent(transactionNo)}
    );
  }

  getPaymentInformation(id: string): void {
    this.isPaymentInformationBusy = true;
    this.requestService.get<PaymentInformationResult[]>(
      `purchase-transaction/payment-method-information/${id}`,
      obj => {
        this.paymentInformation = obj;
        ScriptLoader.loadScript('/assets/js/payment.js', 'payment');
      },
      failed => {
        console.log(failed);
      },
      error => {
        console.log(failed);
      },
      () => {
        this.isPaymentInformationBusy = false;
      });
  }

  getArray(count: number): any[] {
    return new Array(count);
  }
}

The crash error message:

/Users/marko/appname/web/dist/web/server/main.js:111913
              throw error;
              ^

TypeError: (element || document.body).getBoundingClientRect is not a function
    at reflow (/Users/marko/appname/web/dist/web/server/main.js:218104:37)
    at ngbRunTransition.animation (/Users/marko/appname/web/dist/web/server/main.js:226946:11)
    at ngbRunTransition (/Users/marko/appname/web/dist/web/server/main.js:218181:17)
    at Object.next (/Users/marko/appname/web/dist/web/server/main.js:226944:7)
    at ConsumerObserver.__webpack_modules__.3317.ConsumerObserver.next (/Users/marko/appname/web/dist/web/server/main.js:89909:33)
    at SafeSubscriber.__webpack_modules__.3317.Subscriber._next (/Users/marko/appname/web/dist/web/server/main.js:89876:26)
    at SafeSubscriber.__webpack_modules__.3317.Subscriber.next (/Users/marko/appname/web/dist/web/server/main.js:89847:18)
    at /Users/marko/appname/web/dist/web/server/main.js:95558:32
    at OperatorSubscriber._this._next (/Users/marko/appname/web/dist/web/server/main.js:92037:21)
    at OperatorSubscriber.__webpack_modules__.3317.Subscriber.next (/Users/marko/appname/web/dist/web/server/main.js:89847:18)

A server error has occurred.
node exited with 1 code.

The error message is what throws me. It seems like I'm trying to call getBoundingClientRect function but I don't have that in any of my codes.

Any idea on how to fix this?

CodePudding user response:

You'll probably fix your issue by not sending AJAX calls during SSR. Therefore you have to use the

After this, you'll receive the data through the main.server.ts where you can create a provider containing them.

Then, in your page, you can inject this provider and use it

@Component({
  selector: 'artist-show',
  templateUrl: './show.component.html',
  styleUrls: ['./show.component.scss']
})
export class ArtistShowComponent implements OnInit, OnDestroy {

  constructor(private artistService: ArtistService, private route: ActivatedRoute, @Inject(PLATFORM_ID) private platformId: Object, @Inject('ARTIST') private artistInj: Artist) {
    if (isPlatformServer(platformId)) {
      this.artist = artistInj;
    } else {
      this.artistService.getArtist(parseInt(this.route.snapshot.paramMap.get('id')));
    }
  }

  artist: Artist | null = null;
}

My suggestion somehow, would be to only pass a single object all the way from .NET Core to the angular provider. Makes it easier when the app grows.

CodePudding user response:

Sorry, I misread your question. The issue is that you're using something (I guess it's ngbRunTransition) that calls getBoundingClientRect.

Obviously this method isn't known to Node since Node doesn't have the window object. You'll have to look what component/directive in your application is calling the getBoundingClientRect method, and remove it from the DOM using *ngIf, similarily as the other answer.

export class ArtistShowComponent {
  constructor(@Inject(PLATFORM_ID) private platformId: Object) {
    this.isServerSide = isPlatformServer(platformId);
  }

  isServerSide: boolean;
}

In html

<!-- Remove the div from the DOM -->
<div [ngbRunTransition]="'slide'" *ngIf="!isServerSide"></div>

You'll have to find out what component uses the transition, and remove this from the DOM if server-side.

  • Related