The provided content is a comprehensive guide on using Akita for state management in Angular applications, including a tutorial on building a CRUD application with Akita.
Abstract
The article delves into the use of Akita as a state management solution for Angular applications, emphasizing its simplicity and effectiveness compared to other libraries like NgRx and NGXS. It covers the basics of state management, the architecture of Akita, and its core components such as Store and Query. The author illustrates the creation of a sample CRUD application for course management, demonstrating how Akita streamlines data flow and state updates. The guide includes setting up the project, defining models, creating services, and configuring routes, along with addressing Cross-Origin Resource Sharing (CORS) issues through a proxy configuration. The article aims to showcase Akita's ability to reduce boilerplate code and provide a more intuitive approach to managing application state.
Opinions
The author positively notes Akita's simplicity as its most attractive feature, particularly when compared to other state management systems.
Akita is praised for saving developers the hassle of writing extensive boilerplate code, which is often required in other state management libraries.
The article suggests that Akita's approach to state management, which involves a single source of truth and entity stores, is more efficient for Angular applications than traditional component-to-component data passing.
The author implies that Akita's EntityStore and QueryEntity are well-suited for real-world applications that deal with entities like Products, Courses, and Users.
The use of a REST API built with Spring Boot to interact with the Angular application is presented as a common and practical approach for data operations in Angular applications.
The guide concludes by reinforcing the idea that Akita simplifies the process of building Angular applications by reducing the complexity of state management.
Angular: State Management with Akita
Akita is essentially a state management solution for Angular. In my previous posts, I have discussed about other state management libraries for Angular such as NgRx and NGXS.
In this piece, I intend to cover the below topics.
State management in a nutshell
What is Akita?
Building a sample CRUD application with Akita
State Management in a Nutshell?
Theoretically, the application state is the entire memory of the application. In simple terms, the application state is composed of data received by API calls, user inputs, presentation UI state, application preferences, etc. A simple, concrete example of an application state would be a list of customers maintained in a CRM application.
Let’s try to understand the application state in the context of an Angular application. As you are well aware, an Angular application is typically made up of many components. Each of these components has its own state and has no awareness of the state of the other components. In order to share information between parent-child components, we use @Input and@Output decorators. However, this approach is practical only if your application consists of a few components, as shown below.
When the number of components grows, it becomes a nightmare to pass the information between components solely via @Input and @Output decorators. Let’s take the following figure to elaborate on this.
If you have to pass information from component three to component six, you will have to hop four times and involve three other components. As you can see, it’s a very cumbersome and error-prone way of managing the state. Having a state management solution such Akita can come in handy in a situation like this. It will simply transform the above data transfer model something similar to the below.
According to this architecture, data flows between the store and the components, instead of from component to component.
What is Akita?
Akita is a simple and an effective state management solution for Angular applications. It is built on top of RxJS and inspired by models like Flux and Redux. Compared to other state management systems, the most attractive feature of Akita is its simplicity. It saves you the hassle of writing boilerplate code to manage the application state.
Akita is primarily consisted of two main modules.
Store
Query
Store
A Store is a single object which contains the store state and serves as the “single source of truth”. You can define a store as shown below. It simply allows you to save any type of data inside the store state.
EntityStore: In real world applications, you generally work with entities such ash Products, Courses, Users, and etc. In order to simplify the process of working with entities, Akita has introduced a more specific store, namely EntityStore . For the most part, the stores you’ll require in your applications will be entity stores. You can think of an entity store as a table in a database, where each table represents a flat collection of entities. Following code indicates, how you would use EntityStore to manage ToDo objects.
An application can have multiple entity stores to manage different entities. Each of these entity stores represents a dedicated slice in the overall application state. Note that, @StoreConfig({ name: 'todos' }) decorator is used to indicate the name of the relevant state slice. For example, todos state slice contains all the data related to ToDoStore .
Entity stores exposes a set of methods which you can use to update the state. These methods will generally be invoked from within Angular services. Following are some of the commonly used state modifier methods.
set(): Replace current collection with the provided collection, and resets the active entity
add(): Add an entity or entities to the store
update(): Update an entity or entities in the store
remove(): Remove one or more entities from the store
upsert(): Insert or update an entity. Creates a new entity when no entity matches the id; otherwise, it performs an update
Query
A Query is a class which enables you to query the store. You can think of the query as being similar to database queries. Its constructor function receives a reference to its own store and optionally other query classes. A typical query class would look like below.
QueryEntity: The Entity Query is similar to the general Query, with additional functionality tailored for EntityStore. Following code indicates, how QueryEntity can be used to query ToDo entity store.
QueryEntity a set of methods to retrieve data from the store. Following are some of these methods.
selectAll(): Select the entire store’s entity collection
selectMany(): Select multiple entities from the store
selectEntity(): Select an entity or a slice of an entity
Building a Sample CRUD Application
Throughout the rest of this article, we will be building a simple course management system by using Akita. As shown below, you will be able to perform all the CRUD operations on the course entity via this simple web application.
Project structure
As the following figure illustrates, our application will consist of two primary modules, namely App and Course. The course module, in turn, will have two custom components, namely Course List and Create Course.
REST API
In general, an Angular application interacts with a REST API to perform CRUD operations on data.
Therefore, I implemented a simple REST API in Spring Boot that exposes the below endpoints. We will use this API to connect from the Angular application and carry out data operations.
// Retrieve all courses
GET http://localhost:8080/api/courses
// Create a course
POST http://localhost:8080/api/courses
// Delete a course
DELETE http://localhost:8080/api/courses/{courseId}
// Update a course
PUT http://localhost:8080/api/courses/{courseId}
Full source code
You can find the complete source code of this sample application on GitHub. Please note that I have also added the executable JAR file (course-1.0.0-SNAPSHOT.jar) of the Spring Boot application (REST API) to the same repository.
Setting Up the Project
Software versions used
Angular CLI: 8.0.1
Node: 11.6.0
Angular: 8.0.2
Akita: 4.23.0
Bootstrap: 4.4.1
Project initialization
Step 1: Execute the below command and create a new project.
ng newakita-crud-app
Step 2: We will use Bootstrap to add styles to our application. You can install Bootstrap with the command below.
npm install bootstrap --save
Step 3: Import Bootstrap by updating the angular.json file as shown below.
Step 4: Install Akita dependencies.
npm install @datorama/akita --save
Creating and Setting Up the “Course” Feature Module
Generating the “course” module
As stated earlier, our application consists of two major modules, App and Course. Now is the time to create the Course module with the below command.
ng generatemodule course
The command will create a sub-folder named course directly under the app folder. In addition, a new file named course.module.ts will be created and placed under the app/course folder.
The following is the initial version of course.module.ts file. Note that this file will be altered downstream to declare components and to declare service providers.
Defining the “course” model
As the next step, you have to define the model interface that represents the Course entity. Create a file called course.model.ts and place it under the app/course/model folder. The content of this file should be as follows.
Adding Akita Artifacts to “Course” Module
As the next step, you have to define Akita artifacts such as entity store and query entity. These artifacts will be created inside a directory called store which in turn is located under the app/course directory.
Defining Akita Entity Store (course.store.ts)
Special Notes:
As you can see, we have overridden the default entity state and defined an additional attribute with the name areCoursesLoaded . This is a boolean flag which is used to check whether the course entities have already been saved in the state.
We have defined the below custom operation in order to save the course entities in the state and also to mark the areCoursesLoaded flag as true.
Service is used to interact with the REST API and perform data operations. In order to define the service class, create a file named course.service.ts and place it under the app/course/services folder. This service class will be responsible for two primary tasks.
Invoking the backend API and performing the data operations.
Updating the application state with the functions provided by Akita entity store.
Once the service class is defined, you have to register it in the course.module.ts file as shown below.
Creating Components and Defining Routes
As we discussed earlier, our application is made up of two main modules, namely App and Course. The course module consists of two components, namely courses-list and create-course.
Our next step is to create these two components and define the corresponding routes. Note that the courses-list and create-course directories will be created under the app/course/component directory.
Defining “courses-list” component
Template: courses-list.component.html
Component: courses-list.component.ts
Special notes:
This component is responsible for facilitating the list, updating, and deleting operations.
Defining “create-course” component
Template: create-course.component.html
Component: create-course.component.ts
Declaring the components in the Course module
You have to declare the above components in the course.module.ts file, which is already created. In addition, these components will be used in the app.module.ts file to defined the routes. In order to expose these components to the app.module.ts file, you have to define these components in the exports array. Please note that I have also imported FormsModule and HttpClientModule which will be used by templates and service respectively.
Configuring routes
Now is the time to define the routes and associate corresponding components with those routes. This has to be done in the app.module.ts, as shown below.
Defining the router outlet
As the final step, you have to define the router outlet in the app.component.html.
Configuring a Proxy to Access the REST API
As mentioned at the start of this article, we are using a simple REST API written in Spring Boot to connect from the Angular application.
The Spring Boot application runs on localhost:8080, whereas the Angular application runs on localhost:4200. This mismatch will cause a Cross-Origin Resource Sharing (CORS) error when the Angular application tries to access the REST API. To overcome this issue we have to create a proxy.
Creating a proxy file
Create a file called proxy.conf.json inside the project’s root folder (the same level where package.json file exists), and add the below content to it.
Registering the proxy file
In the CLI configuration file, angular.json, add the proxyConfig option to the serve target:
Running the Application
The application should be started as a two-step process. You have to first start the Spring Boot application (REST API) and then the Angular application.
Running the Spring Boot application (REST API)
The Spring Boot application is packaged into an executable JAR file with the name course-1.0.0-SNAPSHOT.jar and placed here (GitHub).
Note that you have to have Java 8 installed on your system to execute this JAR file. If Java 8 is installed, you can execute the below command and start the application.
The Angular application can be started by executing the below command.
ng serve
When the application is started successfully, navigate to http://localhost:4200/courses from your browser, and you should see the below screen.
Conclusion
The prime objective of this story was to provide a step-by-step guide to build an Akita-based Angular application. If you have read my previous posts about NgRx and NGXS, you would understand that Akita saves you the hassle of writing boilerplate code.