Search code examples
angularangular-universalangular-cdkangular-cdk-virtual-scrollangular-ssr

Angular: cdk-virtual-scroll-viewport- scrollWindow attribute not working in angular ssr (universal), giving error- document is not defined


On using cdk-virtual-scroll-viewport in Angular SSR app and adding scrollWindow attribute, I am getting the following error:-

document is not defined
    at _CdkVirtualScrollableWindow (...\test_cdk_scroll_ssr\node_modules\@angular\cdk\fesm2022\scrolling.mjs:1659:26)
    at NodeInjectorFactory.CdkVirtualScrollableWindow_Factory (...\test_cdk_scroll_ssr\node_modules\@angular\cdk\fesm2022\scrolling.mjs:1667:14)
    at getNodeInjectable (...\test_cdk_scroll_ssr\node_modules\@angular\core\fesm2022\core.mjs:5984:44)
    at searchTokensOnInjector (...\test_cdk_scroll_ssr\node_modules\@angular\core\fesm2022\core.mjs:5911:16)
    at lookupTokenUsingNodeInjector (...\test_cdk_scroll_ssr\node_modules\@angular\core\fesm2022\core.mjs:5860:34)
    at getOrCreateInjectable (...\test_cdk_scroll_ssr\node_modules\@angular\core\fesm2022\core.mjs:5772:23)
    at ɵɵdirectiveInject (...\test_cdk_scroll_ssr\node_modules\@angular\core\fesm2022\core.mjs:11050:19)
    at ɵɵinject (...\test_cdk_scroll_ssr\node_modules\@angular\core\fesm2022\core.mjs:1106:42)
    at NodeInjectorFactory.factory (...\test_cdk_scroll_ssr\node_modules\@angular\core\fesm2022\core.mjs:3325:29)
    at getNodeInjectable (...\test_cdk_scroll_ssr\node_modules\@angular\core\fesm2022\core.mjs:5984:44

I followed the official example of scrollWindow attribute in an SSR app and it gave me the above error. This example works fine on non SSR angular apps.

My Code (exactly same as in the example)

app.component.ts:-

import { ChangeDetectionStrategy, Component } from '@angular/core';
import {ScrollingModule} from '@angular/cdk/scrolling';

@Component({
  selector: 'app-root',
  styleUrl: './app.component.scss',
  templateUrl: './app.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: true,
  imports: [ScrollingModule]
})
export class AppComponent {
  items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);
}

app.component.html

<div class="example-header">Content before</div>
<cdk-virtual-scroll-viewport scrollWindow itemSize="50">
  <div *cdkVirtualFor="let item of items" class="example-item">{{item}}</div>
</cdk-virtual-scroll-viewport>
<div class="example-footer">Content after</div>

app.component.scss:-

.example-item {
    height: 50px;
  }
  
  .example-header,
  .example-footer {
    height: 100px;
    background: lightgray;
  }

Maybe it does not work because window is not available on server side?

Is there any way to make the scrollWindow attribute work in Angular SSR apps? thanks!


Solution

  • Yes. Make the virtual scroll render only when it's the browser, we can use isPlatformBrowser and solve it:

    import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
    import { NgIf } from '@angular/common';
    import {ScrollingModule} from '@angular/cdk/scrolling';
    
    @Component({
      selector: 'app-root',
      styleUrl: './app.component.scss',
      templateUrl: './app.component.html',
      changeDetection: ChangeDetectionStrategy.OnPush,
      standalone: true,
      imports: [ScrollingModule, NgIf]
    })
    export class AppComponent {
      items = Array.from({length: 100000}).map((_, i) => `Item #${i}`);
      isBrowser: boolean;
    
      constructor( @Inject(PLATFORM_ID) platformId: Object) {
          this.isBrowser = isPlatformBrowser(platformId);
      }
    }
    

    HTML

    <ng-container *ngIf="isBrowser">
        <div class="example-header">Content before</div>
        <cdk-virtual-scroll-viewport scrollWindow itemSize="50">
          <div *cdkVirtualFor="let item of items" class="example-item">{{item}}</div>
        </cdk-virtual-scroll-viewport>
        <div class="example-footer">Content after</div>
    </ng-container>
    

    By the way, the component works using the window or document object, so it is pointless to render on the server. It's a browser only functionality, due to use of window or document.