2 years ago

#24346

test-img

borjaj

NG-ZORRO and Angular CDK infinite scroll is not working well

I'm using cdk virtual scrolling in an Angular project with Ng-zorro. I have a list of collapse component and in that list is where I use the virtual scrolling.

The problem is that when I open a collapse, if the content is bigger than the window heigth, when I scroll another collapse is open.

I supose that it occours because when the virtual scrolling load more content it gives an id for each collapse component which changes every time I make scrolling. But that it's a vague idea and I didn't found info about that.

This is my hmtl list file:

<div class="fair">
  <h3>{{exhibition.toString()}}</h3>
</div>
<nz-radio-group class="radio-buttons" [(ngModel)]="userStats" [hidden]="!isAdmin && !isManager">
  <label nz-radio-button [nzValue]="true" (click)="clickedMyVisits()">My Visits</label>
  <label nz-radio-button [nzValue]="false" (click)="clickedAllVisits()">All Visits</label>
</nz-radio-group>
<cdk-virtual-scroll-viewport appendOnly itemSize="41">
  <app-visit-info
    *cdkVirtualFor="let visit of visitDataSource"
    [visit]="visit"
    [showUsername]="isAdmin || isManager"
    [username]="this.username.toUpperCase()">
  </app-visit-info>
</cdk-virtual-scroll-viewport>

<ng-template #loading>
  <nz-spin nzTip="Loading..." class="spin"></nz-spin>
</ng-template>

<ng-template #empty>
  <nz-empty class="empty"></nz-empty>
</ng-template>

This is my collapse component:

<nz-collapse>
  <nz-collapse-panel [nzHeader]="showUsername ? visit.toPanelUserString() : visit.toPanelString()">
    <div *ngIf="visit; else loading">
      <div class="title">
        <h2>{{visit.name}}</h2>
        <h2 *ngIf="visit.visitType">{{visit.visitType?.description}}</h2>
        <h2>{{visit.systemDate | date: "dd/MM/yyyy HH:mm"}}</h2>
      </div>
    </div>
  </nz-collapse-panel>
</nz-collapse>

This is my ts file:

@Component({
  selector: 'app-visit-list',
  templateUrl: './visit-list.component.html',
  styleUrls: ['./visit-list.component.less'],

})
export class VisitListComponent implements OnInit, OnDestroy {

  visits$!: Observable<Visit[]>;
  user$!: Observable<User>;
  companyCode!: string;
  username!: string;
  exhibitionCode!: string;
  exhibition!: Exhibition;
  userStats = true;
  visitQuery: VisitQuery = {};
  visitDataSource!: VisitDataSource;
  isAdmin!: boolean;
  isManager!: boolean;
  subscriptions$: Subscription[] = [];

  constructor(private visitService: VisitService,
              private activatedRoute: ActivatedRoute,
              private authService: AuthService,
              private store: Store) {
                this.visitDataSource = new VisitDataSource(this.visitService, this.visitQuery, this.companyCode);
              }

  ngOnDestroy(): void {
    this.subscriptions$.forEach(sub => sub.unsubscribe());
  }

  ngOnInit(): void {
    this.subscriptions$.push(this.store.select(state => state.companyCode.companyCode).subscribe(
      companyCode => {
        this.companyCode = companyCode;
        this.loadData();
      }
    ));
  }

  loadData(){
    this.username = this.activatedRoute.snapshot.paramMap.get('username')!;
    this.companyCode = this.activatedRoute.snapshot.paramMap.get('companyCode')!;
    this.exhibitionCode = this.activatedRoute.snapshot.paramMap.get('exhibitionCode')!;

    this.exhibition = GlobalExhibition.exhibition;

    this.isAdmin = this.authService.hasAdminRole();
    this.isManager = this.authService.hasManagernRole();

    this.visitQuery = {
      username: this.username,
      exhibitionCode: this.exhibitionCode,
      visitUsername: this.username
    }

    this.clickedMyVisits();
  }

  clickedAllVisits() {
    this.visitQuery = {
      username: this.username,
      exhibitionCode: this.exhibitionCode,
    }

    this.visitDataSource = new VisitDataSource(this.visitService, this.visitQuery, this.companyCode);
  }

  clickedMyVisits() {
    this.visitQuery = {
      username: this.username,
      exhibitionCode: this.exhibitionCode,
      visitUsername: this.username
    }

    this.visitDataSource = new VisitDataSource(this.visitService, this.visitQuery, this.companyCode);
  }
}

class VisitDataSource extends DataSource<Visit> {
  public itemsInMemory = Array.from<Visit>({length: 0});
  private itemChanges$: BehaviorSubject<Visit[]> = new BehaviorSubject<Visit[]>([]);
  private destroy$: Subject<boolean> = new Subject();
  private pageSize = 25;
  private lastLoadedPage = 0;

  constructor(private visitService: VisitService,
              private visitQuery: VisitQuery,
              private companyCode: string){
    super();
    visitQuery.pageSize =  this.pageSize;
    this.itemChanges$ = new BehaviorSubject(this.itemsInMemory);
    this.getInformation();
  }

  connect(collectionViewer: CollectionViewer){
    collectionViewer.viewChange.pipe(takeUntil(this.destroy$)).subscribe(range => {
      const currentPage = Math.floor(range.end / this.pageSize);
      if(currentPage > this.lastLoadedPage){
        this.lastLoadedPage = currentPage;
        this.getInformation();
      }
    });
    return this.itemChanges$;
  }

  getInformation(){
    this.visitQuery.offset = this.lastLoadedPage * this.pageSize;
    this.visitQuery.companyCode = this.companyCode;
    if(this.visitQuery.exhibitionCode){
      this.visitService.findVisitsPagination(this.visitQuery).subscribe(data =>{
        data.forEach(visit => {
          this.itemsInMemory.push(visit);
          this.itemChanges$.next(this.itemsInMemory);
        })
      });
    }
  }

  disconnect(){
    this.destroy$.next(true);
    this.destroy$.complete();
  }
}

Thank you everyone! <3

html

angular

angular-cdk

ng-zorro-antd

angular-cdk-virtual-scroll

0 Answers

Your Answer

Accepted video resources