Вы когда-нибудь работали с большими списками данных и задавались вопросом, как эффективно представить их пользователю? Возможно, вы использовали кнопку «загрузить больше» или разбиение на страницы, чтобы ограничить перегрузку, которая может возникнуть при выгрузке огромного списка в ваше приложение users (l).

Представляем виртуальную прокрутку в Angular.

ScrollingModule принимает большой список данных и динамически загружает и выгружает данные из DOM только тогда, когда они попадают в представление пользователей. Это означает, что по мере того, как пользователь прокручивает список, будут появляться новые элементы, а те, которые исчезли из поля зрения пользователей, будут удалены.

Давайте посмотрим, как можно использовать Scrolling Module.

Предпосылки

У вас должен быть установлен Angular 7 CLI (как минимум). Чтобы убедиться в этом, войдите в свой терминал или командную строку и введите команду:

ng version

Загляните сюда, чтобы узнать, как установить Angular CLI, если у вас не установлена ​​Angular или версия CLI ниже 7.

Вам также понадобится, чтобы создать новый проект Angular, перейдите сюда, чтобы узнать, как сгенерировать новый проект с помощью терминала, если вы не уверены.

Ресурсы

Код финального проекта на GitHub.

Шаг 1

В терминале (или в командной строке) устанавливается CDK вместе с материалами и анимациями. Введите следующую команду:

npm i --save @angular/cdk @angular/material @angular/animations

Затем добавьте готовую тему Angular в файл styles.css:

@import ‘@angular/material/prebuilt-themes/deeppurple-amber.css’;

Шаг 2

В свой app.module.ts файл импортируйте ScrollingModule и MatCardModule (мы будем использовать карточки для представления наших данных).

// app.module.ts
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {ScrollingModule} from '@angular/cdk/scrolling';

import {AppComponent} from './app.component';
import {MatCardModule} from '@angular/material';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    ScrollingModule,
    MatCardModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Шаг 3

Создайте фиктивные данные в новом файле с именем mock-data, я создал несколько фиктивных данных от команд NFL, но вы можете использовать все, что захотите:

// mock-data.ts
export const teams = [
  {
    name: 'Arizona Cardinals',
    league: 'NFC West',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/ARI.svg'
  },
  {
    name: ' Atlanta Falcons',
    league: 'NFC South',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/ATL.svg'
  },
  {
    name: 'Baltimore Ravens',
    league: 'AFC North',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/BAL.svg'
  },
  {
    name: 'Buffalo Bills',
    league: 'AFC East',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/BUF.svg'
  },
  {
    name: 'Carolina Panthers',
    league: 'NFC South',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/CAR.svg'
  },
  {
    name: 'Chicago Bears',
    league: 'NFC North',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/CHI.svg'
  },
  {
    name: 'Cincinnati Bengals',
    league: 'AFC North',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/CIN.svg'
  },
  {
    name: 'Cleveland Browns',
    league: 'AFC North',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/CLE.svg'
  },
  {
    name: 'Dallas Cowboys',
    league: 'NFC East',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/DAL.svg'
  },
  {
    name: 'Denver Broncos',
    league: 'AFC West',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/DEN.svg'
  },
  {
    name: 'Detroit Lions',
    league: 'NFC North',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/DET.svg'
  },
  {
    name: 'Green Bay Packers',
    league: 'NFC North',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/GB.svg'
  },
  {
    name: 'Houston Texans',
    league: 'AFC South',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/HOU.svg'
  },
  {
    name: 'Indianapolis Colts',
    league: 'AFC South',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/IND.svg'
  },
  {
    name: 'Jacksonville Jaguars',
    league: 'AFC South',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/JAX.svg'
  },
  {
    name: 'Kansas City Chiefs',
    league: 'AFC West',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/KC.svg'
  },
  {
    name: 'Los Angeles Chargers',
    league: 'AFC West',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/LAC.svg'
  },
  {
    name: 'Los Angeles Rams',
    league: 'NFC West',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/LA.svg'
  },
  {
    name: 'Miami Dolphins',
    league: 'AFC East',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/MIA.svg'
  },
  {
    name: 'Minnesota Vikings',
    league: 'NFC North',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/MIN.svg'
  },
  {
    name: 'New England Patriots',
    league: 'AFC East',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/NE.svg'
  },
  {
    name: 'New Orleans Saints',
    league: 'NFC East',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/NO.svg'
  },
  {
    name: 'New York Giants',
    league: 'NFC South',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/NYG.svg'
  },
  {
    name: 'New York Jets',
    league: 'AFC East',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/NYJ.svg'
  },
  {
    name: 'Oakland Raiders',
    league: 'AFC West',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/OAK.svg'
  },
  {
    name: 'Philadelphia Eagles',
    league: 'NFC East',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/PHI.svg'
  },
  {
    name: 'PittsBurgh Steelers',
    league: 'AFC North',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/PIT.svg'
  },
  {
    name: 'San Francisco 49ers',
    league: 'NFC West',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/SF.svg'
  },
  {
    name: 'Seattle Seahawks',
    league: 'NFC West',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/SEA.svg'
  },
  {
    name: 'Tampa Bay Buccaneers',
    league: 'NFC South',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/TB.svg'
  },
  {
    name: 'Tennessee Titans',
    league: 'AFC South',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/TEN.svg'
  },
  {
    name: 'Washington Redskins',
    league: 'NFC East',
    image: 'https://static.nfl.com/static/site/img/logos/svg/teams/WAS.svg'
  }
];

Шаг 4

Создайте teams.service, используя следующую команду в вашем терминале (или командной строке):

ng g s --spec=false teams

Это создаст для вас службу, из которой мы сможем предоставить наши фиктивные данные нашему приложению, поэтому в вашем teams.service.ts файле создайте getTeams() метод для раскрытия ваших команд. teams.service.ts теперь должен выглядеть так:

// teams.service.ts
import {Injectable} from '@angular/core';
import {teams} from './mock-data';

@Injectable({
  providedIn: 'root'
})
export class TeamsService {

  constructor() {
  }

  getTeams() {
    return teams;
  }
}

Шаг 5

Создайте новый компонент с именем teams, используя следующую команду в терминале (или командной строке):

ng g c --spec=false teams

После создания компонента перейдите в свой app.component.html, удалите весь HTML и стандартный текст и добавьте в селектор для нашего нового компонента <app-teams></app-teams>. Теперь мы можем видеть наш компонент в браузере.

Запустите приложение с помощью команды ng serve и убедитесь, что теперь вы видите свой новый teams.component.

ШАГ 6

Теперь в вашем teams.component.ts файле нам нужно создать переменную с именем team и установить ее в пустой массив:

teams = [];

Затем нам нужно импортировать нашу службу Teams в верхней части файла:

import {TeamsService} from '../teams.service';

Затем мы должны внедрить наш сервис в наш компонент, мы делаем это в конструкторе:

constructor(private teamsService: TeamsService) {}

Теперь мы можем получить доступ к getTeams() методу, который мы создали в службе, чтобы получить наши данные. Нам нужно заполнить наш массив компонентов teams данными, предоставленными службой. Итак, мы создадим новый метод с именем getTeams(){}, и в этом методе нам нужно установить локальный массив, равный методу getTeams(), который мы создали в службе:

getTeams(){
  this.teams = this.teamsService.getTeams();
}

Теперь нам нужно убедиться, что мы вызываем этот метод getTeams() при инициализации нашего компонента, и мы делаем это, вызывая его в методе ngOnInit():

ngOnInit() {
  this.getTeams();
}

Теперь мы создали наш компонент, внедрили наш сервис и получили данные, которые нам нужно потреблять. Вы teams.component.ts теперь должны выглядеть так:

// teams.component.ts
import {Component, OnInit} from '@angular/core';
import {TeamsService} from '../teams.service';

@Component({
  selector: 'app-teams',
  templateUrl: './teams.component.html',
  styleUrls: ['./teams.component.css']
})
export class TeamsComponent implements OnInit {

  teams = [];

  constructor(private teamsService: TeamsService) {
  }

  ngOnInit() {
    this.getTeams();
  }

  getTeams(){
    this.teams = this.teamsService.getTeams();
  }

}

Шаг 7

Теперь нам нужно отобразить эти данные во внешнем интерфейсе, в вашем teams.component.ts файле создайте контейнер

<div class="container"> </div>

В этом контейнере нам нужно создать cdk-virtual-scroll-viewport, и это будет контейнер, в котором хранятся наши данные и который позволяет нам прокручивать их.

Одно из свойств области просмотра - itemSize, и оно должно быть равно высоте каждого отображаемого элемента. В нашем примере мы отображаем карты материалов, мы могли бы установить для них любую высоту, но я собираюсь установить нашу на 200px в CSS позже, поэтому установите itemSize на «200» и дайте области просмотра имя класса «viewport». . Теперь ваш HTML должен выглядеть так:

<div class="container">
  <cdk-virtual-scroll-viewport itemSize="200" class="viewport">
    <!--TODO: Add material card-->
  </cdk-virtual-scroll-viewport>
</div>

Далее нам нужно добавить нашу карту материала. Затем в теге mat-card нам нужно перебрать наш массив команд. Обычно в Angular мы использовали бы директиву *ngFor, однако ScrollingModule предоставляет виртуальную версию *ngFor под названием *cdkVirtualFor, и именно эта волшебная директива поможет загружать и выгружать карты из DOM. В *cdkVirtualFor мы можем перебирать элементы так же, как в *ngFor:

<mat-card *cdkVirtualFor="let team of teams"> </mat-card>

Внутри этого тега карты вы можете добавить элементы карты материала, такие как mat-card-title, img mat-card-image и mat-card-content, добавив нужную информацию. Ваш teams.component.html файл теперь должен выглядеть так:

<!--teams.component.html-->
<div class="container">
  <cdk-virtual-scroll-viewport itemSize="200" class="viewport">
    <mat-card *cdkVirtualFor="let team of teams">
      <mat-card-title>{{team.name}}</mat-card-title>
      <img mat-card-image [src]="team.image" [alt]="team.name">
      <mat-card-content>League {{team.league}}</mat-card-content>
    </mat-card>
  </cdk-virtual-scroll-viewport>
</div>

Шаг 8

Последнее, что нам нужно сделать, это добавить немного стиля к нашему компоненту, чтобы открыть наш порт просмотра и в целом сделать вещи немного красивее. Вы можете использовать мой CSS или стиль, как вам нравится

/*teams.component.css*/
.container {
  display: flex;
  align-self: center;
}

mat-card {
  height: 200px;
  margin: 2em;
  background-color: #1B264F;
  color: white;
}

img {
  width: 20%;
  padding-left: 1em;
}

.viewport {
  height: 400px;
  width: 100%;
  background: #C4BBB8;
  margin: 2em 8em 8em;
  box-shadow: 1px 1px 1px grey;
}

Шаг 9

Перейдите к http://localhost:4200 в своем браузере, откройте инструменты разработчика:

Mac: Option + Command + i | Окна: F12

Вы заметите, что в DOM загружены только 3 карточки, несмотря на то, что в нашем списке 32 команды. Прокручивая список вниз, вы увидите, что новые карточки добавляются, а старые удаляются из модели DOM по мере того, как они появляются и исчезают из поля зрения.

Это волшебство виртуальной прокрутки в Angular 7!

Если вы хотите освоить основы Angular, я создал отличный курс под названием Angular Essentials, доступный только на codeToBe.