Redux в Angular. Создание фейкового API для новостей

Aleksandr Serenko
F.A.F.N.U.R
Published in
3 min readNov 24, 2022

В данной статье реализуется фейковый 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

--

--

Aleksandr Serenko
F.A.F.N.U.R

Senior Front-end Developer, Angular evangelist, Nx apologist, NodeJS warlock