Angular - Ленивая загрузка данных (lazy loading)

Внимание

Данный материал является частью цикла статей «Frontend приложение на Angular». Не забудьте посмотреть другие статьи по этой теме :-)

  1. Angular - Подготовка проекта для нового приложения
  2. Angular - навигация и маршрутизация
  3. Angular - HttpClient, сервисы и файлы окружения
  4. Angular - Ленивая загрузка данных (lazy loading)
  5. Angular - обработка ошибок
  6. Angular - декораторы и директивы @Input и @Output
  7. Angular - валидация формы и POST запрос
  8. Angular - Работа с запросами PUT
  9. Angular - Работа с DELETE запросами

Создание нового модуля в Angular

Итак, начнем с команды Angular CLI для создания модуля:

ng g module owner

Давайте проверим модуль Owner:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: []
})
export class OwnerModule { }

Между этим файлом модуля и файлом модуля приложения есть два небольших различия. Первое отличие состоит в том, что в файле модуля приложения у нас есть оператор импорта для BrowserModule, а в файле модуля Owner - оператор импорта для CommonModule. Это потому, что BrowserModule относится только к корневому модулю в приложении.

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

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

Компонент Owner и отложенная загрузка Angular

Начнем с создания файлов компонента owner-list.

Для этого мы собираемся выполнить команду AngularCLI для создания нового компонента:

ng g component owner/owner-list --skipTests

Эта команда создаст требуемую структуру папок, а также импортирует этот компонент в файл owner.module.ts.

Сейчас мы хотим, чтобы при нажатии на меню «Владелец-Действия» отображалось содержимое HTML-файла этого компонента. Итак, сначала, просто для целей тестирования, давайте изменим файл owner.component.html, добавив один абзац (тег <p></p>):

<p>This is owner-list component page.</p>

После этого изменим файл app.module.ts:

RouterModule.forRoot([
      { path: 'home', component: HomeComponent },
      { path: 'owner', loadChildren: () => import('./owner/owner.module').then(m => m.OwnerModule) },
      { path: '404', component : NotFoundComponent},
      { path: '', redirectTo: '/home', pathMatch: 'full' },
      { path: '**', redirectTo: '/404', pathMatch: 'full'}
    ])

С помощью измененной части кода мы настраиваем app.module для загрузки модуля владельца всякий раз, когда кто-то открывает страницу http://localhost:4200/owner. Как вы могли заметить, мы используем свойство loadChildren, что означает, что модуль владельца с его компонентами не будет загружен, пока мы явно не запросим их. Таким образом, мы настраиваем отложенную загрузку Angular из содержимого модуля владельца.

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

Маршрутизация для модуля owner

Давайте изменим owner.module.ts:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

import { OwnerListComponent } from './owner-list/owner-list.component';

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([
      { path: 'list', component: OwnerListComponent }
    ])
  ],
  declarations: [
    OwnerListComponent
  ]
})
export class OwnerModule { }

При такой настройке мы открываем компонент OwnerListComponent на конечной точке http://localhost:4200/owner/list. Более того, мы используем функцию RouterModule.forChild, а не функцию forRoot. Дело в том, что мы должны использовать функцию forRoot только в корневом модуле приложения.

Опять же, лучшая практика для больших проектов - хранить массив маршрутизаторов в отдельном файле, но для простоты этого примера мы храним его внутри функции forRoot.

Теперь нам нужно изменить файл menu.component.html:

<ul class="navbar-nav mr-auto">
  <li class="nav-item">
    <a class="nav-link" [routerLink]="['/owner/list']" routerLinkActive="active" 
      [routerLinkActiveOptions]="{exact: true}"> Owner Actions </a>
   </li>
   <li class="nav-item">
     <a class="nav-link" href="#">Account Actions </a>
   </li>
</ul>

Мы можем проверить результат:

Теперь мы знаем, как настроить маршрутизацию для дочернего модуля, а также для компонента внутри этого модуля.

Интерфейс, подписка и отображение данных

Когда мы переходим в меню «Действия владельца», мы хотим показать пользователю всех владельцев. Это означает, что при загрузке компонента владельца приложение автоматически получает всех владельцев с сервера.

Для этого давайте создадим новую папку внутри папки приложения и назовем ее _ interfaces. Внутри создайте новый файл owner.model.ts.

Измените этот файл:

export interface Owner{
    id: string;
    name: string;
    dateOfBirth: Date;
    address: string;
}

Измените owner-list.component.ts:

import { Component, OnInit } from '@angular/core';
import { RepositoryService } from './../../shared/services/repository.service';
import { Owner } from './../../_interfaces/owner.model';

@Component({
  selector: 'app-owner-list',
  templateUrl: './owner-list.component.html',
  styleUrls: ['./owner-list.component.css']
})
export class OwnerListComponent implements OnInit {
  public owners: Owner[];

  constructor(private repository: RepositoryService) { }

  ngOnInit(): void {
    this.getAllOwners();
  }

  public getAllOwners = () => {
    let apiAddress: string = "api/owner";
    this.repository.getData(apiAddress)
    .subscribe(res => {
      this.owners = res as Owner[];
    })
  }
}

Как вы могли заметить, у нас есть свойство с именем owners, и оно относится к типу Owner array. Затем мы выполняем функцию подписки, которая заполняет это свойство всеми владельцами с сервера. Мы стремимся использовать свойство owners для создания нашей HTML-страницы владельца.

Для этого давайте изменим компонент HTML:

<div class="row"> 
  <div class="offset-10 col-md-2">
    <a href="#">Create owner</a>
  </div>
</div>
<br>
<div class="row">
  <div class="col-md-12">
    <div class="table-responsive">
      <table class="table table-striped">
        <thead>
          <tr>
            <th>Owner name</th>
            <th>Owner address</th>
            <th>Date of birth</th>
            <th>Details</th>
            <th>Update</th>
            <th>Delete</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let owner of owners">
            <td>{{owner.name}}</td>
            <td>{{owner.address}}</td>
            <td>{{owner.dateOfBirth | date: 'dd/MM/yyyy'}}</td>
            <td><button type="button" id="details" class="btn btn-light">Details</button></td>
            <td><button type="button" id="update" class="btn btn-success">Update</button></td>
            <td><button type="button" id="delete" class="btn btn-danger">Delete</button></td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

Мы могли бы разделить этот html-файл на два компонента (родительский компонент: OwnerList и дочерний компонент Owner), но поскольку мы не объяснили, как использовать дочерние компоненты, и мы собираемся сделать это в следующих публикациях, мы собираемся пока оставить это так.

Мы используем несколько базовых классов Bootstrap для создания таблицы, отображающей данные владельца. Внутри этой таблицы мы перебираем всех владельцев (с помощью * ngFor). Затем, используя интерполяцию {{}}, мы показываем свойства владельца на странице. Для свойства dateOfBirth мы используем только форматирование даты, чтобы показать ее в том виде, как мы хотим видеть на экране.

В нашем приложении мы собираемся использовать формат даты как MM/dd/yyyy, но здесь мы собираемся использовать dd/MM/yyyy, чтобы продемонстрировать способ без особых усилий изменить формат с помощью трубок.

Заключение

Прочитав этот пост, вы узнали:

  • Как создать новый модуль Angular и какой импорт использовать
  • Как настроить отложенную загрузку Angular и как это может помочь вашему приложению.
  • Как выполнить HTTP-запрос с подпиской и отобразить результаты на странице
  • Способ переформатирования типа даты при ее отображении.

Спасибо, что прочитали этот пост, надеюсь, он был вам полезен.

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