Redux в Angular. Создание фейкового API для новостей
В данной статье реализуется фейковый API сервис для CRUD новостей.
Для управления новостями будет использоваться PostApiService
, который будет по требуемым запросам возвращать observable
.
PostApiService
будет выглядеть следующим образом:
Возможно сервис выглядит достаточно громоздким, однако сервис достаточно тривиален.
Так сервис представляет собой фейковое API, то необходим токен, который будет представлять собой список новостей:
export const POSTS = new InjectionToken<PostDto[]>('POSTS');
Тогда при инициализации в конструкторе можно прочитать POSTS
и установить список новостей. А так как необходимо добавлять, изменять, удалять новости, то необходимо хранить состояние новостей. Для этого будет использоваться localStorage
.
При инициализации сервиса будет смотреться в localstorage
поле новостей, и если оно есть, то в качестве списка новостей будет использоваться данное значение, а если данных в localstorage
нету, то тогда будет использоваться либо значение из POSTS
если оно задано, либо значение по умолчанию.
Для простоты, сервис имеет два свойства:
private ids: string[];
private entities: Record<string, PostDto>;
Свойство ids
— список идентификаторов (uuid
) новостей.
Свойство entities
— это словарь новостей, где в качестве ключа выступает uuid
новости.
constructor(
private readonly platformService: PlatformService,
private readonly windowService: WindowService,
@Optional() @Inject(POSTS) private readonly posts: PostDto[] | null
) {
let savedPosts: Record<string, PostDto> | null = null;
if (this.platformService.isBrowser) {
const savedPostString = this.windowService.window.localStorage.getItem(POSTS_KEY) ?? null;
if (savedPostString) {
savedPosts = JSON.parse(savedPostString);
}
}
const entities = savedPosts ? Object.values(savedPosts) : this.posts ?? POSTS_DEFAULT;
this.uuids = entities.map((post) => post.uuid);
this.entities = entities.reduce((acc, current) => ({ ...acc, [current.uuid]: current }), {});
}
Стоит отметить, что используется два сервиса: PlatformService
и WindowService
. Это простые обертки над стандартным функционалом фреймворка.
PlatformService
— возвращает признак платформы, в частности является ли запущенная версия браузерной, серверной.
WindowService
— специальный сервис обертка над window
, чтобы не обращаться к глобальному объекту напрямую.
Это необходимо для того, что в различных платформах
angular
может не быть глобального объектаwindow
. Например, в серверной версии естьglobal
, а в браузернойwindow
.
PostApiService
включает в себя следующие методы:
get()
— метод получения списка новостей;getOne()
— метод получения одной новости по uuid;create()
— метод создания новой новости;change()
— метод изменения новости;remove()
— метод удаления новости;clear()
— метод удаления всех новостей;reset()
— метод приведения состояния новостей к значению по умолчанию;save()
— приватный метод для сохранения состояния новостей.
Более детально рассмотрим метод получения списка новостей.
get(): Observable<PostDto[]> {
return new Observable((observer) => {
try {
observer.next(Object.values(this.entities) ?? []);
} catch (error) {
observer.error(error);
}
observer.complete();
});
}
Загрузка списка новостей выглядит следующим образом:
- Создается новый поток
observable
; - Далее берется последнее значение состояния из
entities
и испускается список новостей; - Если во время получения данных происходит ошибка, то тогда испускается ошибка;
- По завершению поток закрывается.
Так как получение списка новостей не влияет на состояние новостей, то никаких манипуляций с данными не происходит.
Другая ситуация с методом создания новости:
create(postCreate: PostCreate): Observable<PostDto> {
return new Observable((observer) => {
try {
let post = this.entities[postCreate.uuid];
const now = new Date().toISOString();
if (post) {
post = {
...post,
...postCreate,
updated: now
};
} else {
post = {
...postCreate,
created: now,
updated: now,
likes: 0,
views: 0
};
this.uuids.push(post.uuid);
}
this.entities[post.uuid] = post;
this.save();
observer.next(post);
} catch (error) {
observer.error(error);
}
observer.complete();
});
}
Создание новой записи выглядит следующим образом:
- Создается новый поток
observable
; - Далее ищется запись с переданным
uuid
- Если нет сущности с новым
uuid
(entities[uuid]
), то создается новая запись, иначе изменяется ранее созданная. В случае если запись уже существует, то формально происходит операция изменения записи, а не создание новой. Это сделано для упрощения демо приложения и не является рекомендованным решением. - После успешного создания/изменения записи происходит сохранение состояния новостей, путем записи всех новостей в
localstorage
; - Если во время получения данных происходит ошибка, то тогда испускается ошибка;
- По завершению поток закрывается.
Остальные методы в сервисе работают аналогично.
Ссылки
Предыдущая статья — Создание базовых классов.
Следующая статья — Создание UI компонентов для отображения новостей.
Все исходники находятся на github, в репозитории:
Для того, чтобы посмотреть состояние проекта на момент написания статьи, нужно выбрать соответствующий тег — redux
.
Подписывайтесь на блог, чтобы не пропустить новые статьи про Angular, и веб-разработку. Medium | Telegram| VK |Tw| Ln