Angular 12+ crud with crud-collection

IvanPerun
5 min readOct 18, 2022

Introducing our Angular 12+ CRUD Application

We will learn how to build an Angular 12 front-end application that fetches data from a REST API of projects:

  • Each project has an id, name, description, and status.
  • Users would be able to create, get, update, and delete projects.
  • Users can search for projects by name, sort, and status.
  • projects have pagination.

The REST API Endpoints

We’ll be building an Angular 12 frontend app for a presumed REST API exporting the following REST API endpoints:

  • POST /api/projects create a new project
  • GET /api/projects get all projects
  • GET /api/projects/:id get a project by :id
  • PUT /api/projects/:id update a project by :id
  • DELETE /api/projects/:id delete a project by :id
  • DELETE /api/projects delete all projects
  • GET /api/projects?name=[keyword]&sort=[asc/desc]&page=1&per_page=15 find and sort/paginate projects list.

Angular 12+ App Structure

This is a component of our CRUD app:

ProjectsListComponent which displays the list of projects.

The components use the Collection, Item methods for making CRUD operations against the REST API and sending HTTP requests to the REST and process responses.

Step 1 — Creating a New Angular 12+ Project

Let’s start by generating a new Angular 12+ project using CLI. You need to run the following command:

$ ng new AngularCrudCollection

The CLI will ask you a couple of questions — If Would you like to add Angular routing? Type y for Yes and Which stylesheet format would you like to use? Choose SCSS.

Step 2— Add Collection to Angular12+

Next 2.1: Add HttpClientModule to AppModule.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule
],
bootstrap: [AppComponent]
})
export class AppModule {
}

Collection/Item used HttpClient to make a request call.

Next 2.2: Install the crud-collection library npm i crud-collection.

Next 2.3: Add mapResponse to AppComponent.

import { Component, OnInit } from '@angular/core';
import {
Collection,
Item,
CrudService
} from "crud-collection";

Collection.prototype.mapResponse = (res) => {
return {
items: res.data,
meta: res.meta.pagination
};
};

Item.prototype.mapResponse = (res) => {
return res.data;
};

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {constructor(
private _crud: CrudService
) {
this._crud.apiUrl = environment.apiUrl;
}
}

Collection works with a list of items, so we need to transform the response of our backend, this scope of code:

Collection.prototype.mapResponse = (res) => {
return {
items: res.data,
meta: res.meta.pagination
};
};
// Also set backend api url
this._crud.apiUrl = environment.apiUrl;

After call collection.reload().subscribe() The collection put loaded items to collection.items property (use in template ).

Item works with the single entity of data, so we need to transform the response of our backend, this scope of code:

Item.prototype.mapResponse = (res) => {
return res.data;
};

After call item.load().subscribe() Item put loaded data to item.data property.

Step 3— Generating Angular 12 Project List Components

Next, we need to generate a bunch of components and a service using the Angular CLI as follows:

$ ng g c components/products-list

We have generated component projects-list.

Step 4— Use Collection instance for search/create/update/delete projects

Next, we need to create a Collection instance:

@Component({
selector: 'projects-list',
templateUrl: './projects-list.component.html',
styleUrls: ['./projects-list.component.scss']
})
export class ProjectsListComponent implements OnInit {
projectsCollection: CollectionModel;

ngOnInit() {
// onLoad$ -- invoce when collection loaded
this.projectsCollection.onLoad$.subscribe( () => {

});
this.reload();
}

reload() {
this.projectsCollection.setParams('page', 1);
this.projectsCollection.reload().subscribe(( res ) => {

})
}

nextPage() {
let page = this.projectsCollection.getParams('page');
this.projectsCollection.setParams('page', +page + 1);
this.projectsCollection.search();
}

constructor(
private _crud: CrudService
) {

this.projectsCollection = new Collection({
api: this._crud.createEntity({ name: 'projects' })
});
}

Template:

<ng-container *ngFor="let project of collection.items$ | async">
<p>{{ project?.name }}</p>
</ng-container>
<ng-container *ngIf="collection.loading$ | async">
Loading...
</ng-container>
<button (click)="nextPage()">Reload</button>

Create a project in the projects list and reload the list:

create(): void {
const dataToCreate = {
name: 'Project 1',
status: 'active'
}

// use it if you don't want to reload list after create new item with collection
// this.collection.hardReloadAfter.creating = false;
this.collection.createItem({ data: dataToCreate }).subscribe(res => {
// send post request by url '/projects'
// collection automatically call collection.reload() and reload projects list
})
}

Update project in projects list and reload list:

update(projectId: number): void {
const dataToUpdate = {
name: 'Project Updated',
status: 'inactive'
}

// use it if you don't want to reload list after create new item with collection
// this.collection.hardReloadAfter.updating = false;

this.collection.updateItem({ id: projectId, data: dataToUpdate }).subscribe( res => {
// send put request by url '/projects/:id'
// collection automatically call collection.reload() and reload projects list
})
}

Delete the project in the projects list and reload the list:

delete(projectId: number): void {
// use it if you don't want to reload list after create new item with collection
// this.collection.hardReloadAfter.deleting = false;

this.collection.deleteItem({ id: projectId }).subscribe( res => {
// send put request by url '/projects/:id'
// collection automatically call collection.reload() and reload projects list
})
}

Search projects by name/sort/page and cancel all previous requests:

search(): void {
this.collection
.setParams('page', 1)
.setParams('name', 'string for search')
.setParams('sort', 'desc')
.search();
// send query params with url '/projects?page=1&name=string for search&sort=desc'
// search() method automatically cancel previous request
}

Transform each item before putting them into the collection.items:

this.collection = new Collection({
api: this._crud.createEntity({ name: 'projects' })
});

this.collection.mapItems = (item) => {
item['can_edit'] = true; // set some condition
return item;
}

If you do not use Rest Full API, then you can change the path in the collection method, for example:

// url for get data: GET  /projects/listthis.collection = new Collection({
api: this._crud.createEntity({ name: 'projects/list' })
});
// url for create data: POST /projects/createthis.collection.createItem({ data: dataToCreate, path: 'projects/create' }).subscribe();// url for update data: PUT /projects/:id/updatethis.collection.updateItem({ data: dataToUpdate, path: `projects/${ projectId }/update` }).subscribe();// url for delete data: DELETE /projects/:id/deletethis.collection.deleteItem({ data: dataToUpdate, path: `projects/${ projectId }/delete` }).subscribe();

If you use OnPush ChangeDetectionStrategy, use:

<ng-container *ngFor="let project of collection.items$ | async">
<p>{{ project?.name }}</p>
</ng-container>

<ng-container *ngIf="collection.loading$ | async">
Loading...
</ng-container>

<ng-container *ngIf="(collection.loadingCrud$ | async).creating">
Loading on creating...
</ng-container>

<ng-container *ngIf="(collection.loadingCrud$ | async).updating">
Loading on updating...
</ng-container>

Conclusion:

1. You can efficiently work with requests using Collection.
2. You save thousands of lines of code.
3. Collection builds the requests and saves the response into the collection instance.

Enjoy, thanks for reading this far.

https://www.npmjs.com/package/crud-collection

--

--