Frontend
October 16, 2023

Работа с файлами и загрузка данных в Angular

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

В этой статье мы подробно рассмотрим:

  • Как организовать работу с данными в Angular
  • Как получать данные с сервера
  • Различные API для загрузки данных
  • Как загружать и обрабатывать файлы
  • Как отображать данные в интерфейсе

Изучив эту статью, вы сможете уверенно работать с данными и файлами в своих Angular-приложениях. Давайте начнем!

Понимание основ Angular

Перед тем как перейти непосредственно к данным, давайте разберемся с основами Angular. Это поможет нам лучше понять дальнейший материал.

Angular CLI

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

Чтобы установить Angular CLI, нужно выполнить команду:

npm install -g @angular/cli

Затем для создания нового проекта используем:

ng new my-app

Это создаст папку my-app со всеми необходимыми файлами Angular.

Также с помощью Angular CLI можно:

  • Генерировать компоненты, сервисы, модули
  • Запускать проект на разработку
  • Собирать финальную версию

Он сильно ускоряет разработку и позволяет не тратить время на рутину.

Компоненты

Компонент - это основная единица пользовательского интерфейса в Angular. Компонент отвечает за вид и поведение части экрана.

Например, мы можем создать компоненты для отображения списка товаров, формы заказа, кнопки и т.д.

Компонент создается как класс с декоратором @Component:

import {Component} from '@angular/core';

@Component({
  selector: 'app-product',
  templateUrl: './product.component.html'
})
export class ProductComponent {

}

Здесь мы определяем шаблон компонента в product.component.html и CSS стили в product.component.css.

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

Модули

Модули в Angular служат для организации кода приложения.

Модуль объединяет похожие компоненты, сервисы, директивы и трубы (pipes).

Например, мы можем создать модуль для пользовательской части приложения, модуль для администраторской части и т.д.

Модуль определяется следующим образом:

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

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

}

Здесь в declarations мы указываем компоненты данного модуля, а в imports - другие необходимые модули.

Сервисы

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

Например, можно создать сервис для работы с API:

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';

@Injectable()
export class ProductsService {

  constructor(private http: HttpClient) {}

  getProducts() {
    return this.http.get('/api/products');
  }

}

А затем использовать этот сервис в компонентах для загрузки данных о продуктах.

Загрузка данных в Angular

Теперь, когда мы разобрались с основами Angular, давайте поговорим непосредственно о работе с данными.

Основные сценарии при работе с данными:

  • Получение данных с сервера
  • Кэширование данных на клиенте
  • Показ загрузки и ошибок
  • Фильтрация и поиск данных
  • Пагинация для больших списков
  • Обновление данных в реальном времени

Рассмотрим подробнее эти сценарии на примере приложения для просмотра курсов.

Получение данных с сервера

Для загрузки данных с сервера в Angular используется сервис HttpClient из библиотеки @angular/common/http.

Первый шаг - импортировать HttpClientModule в корневой модуль приложения:

import {HttpClientModule} from '@angular/common/http'; 

@NgModule({
  // ...
  imports: [
    // ...
    HttpClientModule
  ]
})

Затем в сервисе можно инжектировать HttpClient и делать запросы к API:

import {HttpClient} from '@angular/common/http';

@Injectable()
export class CoursesService {

  constructor(private http: HttpClient) {}

  getCourses() {
    return this.http.get('/api/courses');
  }

}

А в компоненте подписаться на данные из сервиса:

courses$ = this.coursesService.getCourses();

Таким образом мы получаем данные с сервера для дальнейшего использования в приложении.

Кэширование данных

Чтобы не делать лишние запросы к API, можно кэшировать данные на клиенте.

Для этого в сервисе можно хранить данные в переменной:

coursesCache: any;

getCourses() {
  if (this.coursesCache) {
    return this.coursesCache;  
  }

  return this.http.get('/api/courses')
    .pipe(
      tap(courses => this.coursesCache = courses)  
    );
}

Теперь при повторных запросах будет возвращаться кэшированная версия данных без лишних обращений к API.

Обработка загрузки и ошибок

При загрузке данных важно показывать пользователю прогресс и обрабатывать ошибки.

Для отображения прогресса загрузки можно использовать переменную:

isCoursesLoading = false;

getCourses() {
  this.isCoursesLoading = true;

  return this.http.get('/api/courses')
    .pipe(
      finalize(() => this.isCoursesLoading = false)
    );
}

А в шаблоне отобразить лоадер:

<div *ngIf="isCoursesLoading">
  Loading...
</div>

<div *ngIf="!isCoursesLoading">
  <!-- данные курсов -->
</div>

Для обработки ошибок используем блок catchError:

getCourses() {
  return this.http.get('/api/courses')
    .pipe(
      catchError(err => {
        // показать ошибку пользователю 
        return throwError(err);  
      })
    );
}

Фильтрация и поиск

Чтобы реализовать фильтрацию и поиск данных на клиенте, можно использовать RxJS операторы.

Например, для фильтрации можно применить оператор filter:

// фильтрация по бесплатным курсам
const freeCourses$ = courses$.pipe(
  filter(c => !c.paid) 
);

Для поиска по названию можно использовать операторы map и filter:

const searchCourses$ = courses$.pipe(
  map(courses => {
    return courses.filter(c => {
      return c.title.includes(searchTerm);
    });
  })
);

Это позволит реализовать фильтрацию и поиск на стороне клиента.

Пагинация

Для больших списков данных удобно реализовать пагинацию.

Например, можно загружать данные частями по 10 элементов:

getCourses(page) {
  const limit = 10;
  
  return this.http.get('/api/courses', {
    params: {
      _page: page,
      _limit: limit
    }
  });
}

А на клиенте хранить текущую страницу и вычислять pagination controls:

page = 1;

prevPage() {
  this.page--;
  this.loadCourses();
}

nextPage() {
  this.page++;
  this.loadCourses(); 
}

Это позволит разбить большие списки на страницы.

Обновление данных в реальном времени

Для обновления данных в реальном времени можно использовать WebSockets.

Например, Biblioteca Socket.io позволяет установить соединение между клиентом и сервером для обмена событиями.

На бэкенде реализуем Socket.io сервер, который будет посылать события при изменении данных.

А на фронтенде подключаем Socket.io клиент и слушаем события:

import {io} from 'socket.io-client';

const socket = io();

socket.on('courses update', (data) => {
  // обновить данные
});

Таким образом мы можем в реальном времени обновлять данные на клиенте.

Источники данных в Angular

Давайте теперь рассмотрим, откуда можно получать данные для Angular-приложения.

REST API

Наиболее распространенный способ - использовать REST API на бэкенде. REST - это архитектурный стиль для разработки веб-сервисов.

Основные принципы REST:

  • Клиент-серверная архитектура
  • Stateless - сервер не хранит состояния между запросами
  • Кешируемость - клиенты могут кэшировать данные
  • Единый интерфейс - всегда используется HTTP

REST API возвращает данные в форматах JSON или XML.

Для работы с REST API используем HTTP клиент Angular:

getCourses() {
  return this.http.get('/api/courses'); 
}

Популярные фреймворки для создания REST API:

  • Node.js + Express
  • Java Spring
  • PHP Laravel
  • Python Django/Flask

GraphQL API

GraphQL - это современная альтернатива REST, созданная Facebook.

Основные особенности GraphQL:

  • Позволяет запрашивать только нужные данные
  • Использует систему типов для описания данных
  • Поддерживает запросы с вложенными данными

Пример GraphQL запроса:

query {
  course(id: "1") {
    title
    author {
      name
    }
  }
}

В ответ мы получим только запрошенные поля, без лишней информации.

Для работы с GraphQL есть специальная библиотека Apollo Client.

Firebase

Firebase - это платформа от Google для создания приложений. Она включает облачное хранилище данных Firestore.

Преимущества Firestore:

  • Автоматическая синхронизация данных на устройствах
  • Поддержка оффлайн режима
  • Встроенная аутентификация
  • Гибкая модель данных

Для работы с Firebase в Angular устанавливаем пакет @angular/fire:

import {AngularFirestore} from '@angular/fire/compat/firestore';

constructor(private db: AngularFirestore) {}

getCourses() {
  return this.db.collection('courses').valueChanges(); 
}

Firebase упрощает синхронизацию данных между клиентом и облаком.

Работа с файлами в Angular

Рассмотрим как работать с файлами в Angular приложениях. Основные задачи:

  • Загрузка файлов на сервер
  • Отображение загруженных изображений
  • Валидация файлов на клиенте
  • Обработка прогресса загрузки

Загрузка файлов

Для загрузки файлов используем стандартный HTML элемент <input type="file">.

В компоненте можно получить доступ к выбранным файлам через $event:

<input type="file" (change)="selectFile($event)">

selectFile(event) {
  const file = event.target.files[0];
}

Для отправки на сервер формируем FormData:

const formData = new FormData();

formData.append('file', file);

this.http.post('/upload', formData);

На сервере можно сохранять файлы с помощью библиотеки Multer в Node.js илиUploadMiddleware в .NET.

Отображение изображений

Чтобы показать preview загруженного изображения, можно создать его объект и присвоить в img:

const img = new Image();

img.src = URL.createObjectURL(file);

this.imagePreview = img.src;

А в шаблоне:

<img [src]="imagePreview">

Также для изображений можно реализовать кроппинг и компрессию перед загрузкой.

Валидация файлов

Для валидации файлов на клиенте можно проверять:

  • Тип файла
  • Размер
  • Разрешение для изображений

Например:

const isValid = file.type === 'image/jpeg' && 
  file.size < 2000000 &&
  img.width === 1024 &&
  img.height === 768;

Также можно показывать ошибки валидации пользователю с помощью Angular Forms.

Отображение прогресса

Для показа прогресса загрузки файлов можно использовать библиотеку ngx-progressbar.

В компоненте устанавливаем значение прогресса:

this.progress = 0;

uploadFile() {
  // увеличиваем прогресс
}

А в шаблоне отображаем полосу прогресса:

<ngx-progressbar 
  [value]="progress">
</ngx-progressbar>

Это позволит визуализировать ход выполнения для пользователя.

Отображение данных в интерфейсе

Последний шаг после загрузки данных - отобразить их в интерфейсе приложения. Рассмотрим основные моменты.

Использование структурных директив

Для вывода массивов данных используем встроенные директивы Angular:

  • *ngFor - для итерации по элементам массива
  • *ngIf - для условного отображения блоков
<div *ngFor="let course of courses">
  {{ course.title }}
</div>

<div *ngIf="courses.length === 0">
  No data
</div>

Также можно применять вложенные *ngFor для вывода вложенных данных.

Пайпы

Пайпы позволяют преобразовать данные перед отображением.

Например, можно отформатировать дату:

{{ course.created | date:'medium' }}

Или преобразовать текст:

{{ course.description | slice:0:100 }}...

Полезные встроенные пайпы: date, slice, json, uppercase, number, currency.

Компоненты отображения данных

Для сложных интерфейсов удобно создавать специальные компоненты.

Например, компонент course-item будет отвечать за отображение одного элемента списка курсов.

Это позволяет абстрагировать логику отображения данных от основных контейнерных компонентов.

Заключение

В этой статье мы рассмотрели основные аспекты работы с данными и файлами в Angular:

  • Как организовать загрузку данных используя HttpClient
  • Различные источники данных - REST, GraphQL, Firebase
  • Как загружать и обрабатывать файлы
  • Как кэшировать данные и обновлять их в реальном времени
  • Как отображать данные в шаблонах с помощью директив и компонентов

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