Search code examples
angularserver-side-renderingangular-universal

Page crash after converting to Angular Universal (SSR)


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?


Solution

  • 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.