Вы когда-нибудь работали с большими списками данных и задавались вопросом, как эффективно представить их пользователю? Возможно, вы использовали кнопку «загрузить больше» или разбиение на страницы, чтобы ограничить перегрузку, которая может возникнуть при выгрузке огромного списка в ваше приложение 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.