Documentation
State machines for crud components
Documentation on state machines for crud components
This documentation explains how the CRUD state machines and components are set up to manage the creation, update, and deletion of entities (like "products") in an Angular application that communicates with a Supabase backend.
Overview
- State Machines: Automatically generated classes that handle entity creation, updates, and deletions. They manage form state, API calls, and route navigation, and interact with Supabase through a data service (injected via a facade).
- Components: Smart components that interact with the state machines to manage product-related operations. They rely on signals for reactive state management and implement business logic specific to the entity, such as mapping form data to database structures.
1. CrudEntityUpdateStateMachine
for Updating an Entity
The CrudEntityUpdateStateMachine
class is designed to handle updating an existing entity (e.g., a product) within a Supabase table. It manages the form state, loads the current entity data, handles user interactions, and performs updates.
Key Responsibilities:
- Form State Management: Tracks form values, validity, and dirty state (whether the form has been modified).
- Entity Loading: Loads the entity based on the ID derived from the route parameters.
- Updating and Deleting: Submits the updated form data or deletes the entity based on user actions.
- Routing: Handles route navigation after actions like saving or deleting.
Example Use Case: Updating a Product
Smart Component for Product Detail:
@Component({
selector: 'nitrokit-product-detail',
standalone: true,
imports: [
ProductFormUiComponent, // UI component to handle product form display
ButtonUiComponent,
DrawerUiComponent,
RouterLink,
DeleteModalUiComponent,
BaseLayoutCrudUpdateUiComponent, // Base layout for the update form UI
],
templateUrl: './product-detail.smart-component.html',
styleUrls: ['./product-detail.smart-component.scss'],
})
export class ProductDetailSmartComponent {
readonly #facadeService = inject(FacadeService); // Service to manage data state and communication
// Merges entity data with form value
readonly #mergeEntityWithForm = (
entity: ProductRow,
formValue: Partial<ValidProductFormModel>
): ValidProductFormModel => {
return { ...};
};
// Maps form data to the database update structure
readonly #mapFormToEntity = (form: ValidProductFormModel): ProductUpdate => {
return { ... };
};
// State machine for updating the product
protected readonly updateState = new CrudEntityUpdateStateMachine<
Database,
'products',
ValidProductFormModel
>(
this.#facadeService.productStateMachine, // The entity state machine for products
this.#mergeEntityWithForm,
this.#mapFormToEntity
);
}
Key Functions of the Update State Machine:
#mergeEntityWithForm()
: Merges the currently loaded product data with the form's input values.#mapFormToEntity()
: Maps the form values into the structure required to perform an update in Supabase.updateState.save()
: Submits the form to update the entity in Supabase.
2. CrudEntityCreateStateMachine
for Creating a New Entity
The CrudEntityCreateStateMachine
class is responsible for managing the creation of new entities. It tracks form state and submits data to create a new product in Supabase.
Key Responsibilities:
- Form State Management: Tracks form values, validity, and whether the form has been modified.
- Entity Creation: Maps the form data into the structure required by Supabase and submits it for creation.
- Routing: Navigates to the previous page after successful creation.
Example Use Case: Adding a New Product
Smart Component for Adding a Product:
@Component({
selector: 'nitrokit-product-add',
standalone: true,
imports: [
ProductFormUiComponent, // UI component to handle product form display
ButtonUiComponent,
DrawerUiComponent,
RouterLink,
BaseLayoutCrudCreateUiComponent, // Base layout for the create form UI
],
templateUrl: './product-add.smart-component.html',
styleUrls: ['./product-add.smart-component.scss'],
})
export class ProductAddSmartComponent {
readonly #facadeService = inject(FacadeService); // Service to manage data state and communication
// Maps form data to the structure required for inserting a new product into Supabase
readonly #mapFormToEntity = (form: ValidProductFormModel): ProductInsert => {
return { ... };
};
// State machine for creating a new product
protected readonly createState = new CrudEntityCreateStateMachine<
Database,
'products',
ValidProductFormModel
>(this.#facadeService.productStateMachine, this.#mapFormToEntity);
}
Key Functions of the Create State Machine:
#mapFormToEntity()
: Maps the form values to the format required to create a new product in Supabase.createState.save()
: Submits the form and adds a new product to the database.
Understanding the State Machine Flow
- Facade Service:
- Product State Machine: Interacts with the backend through Supabase, managing data access.
- Session Handling: Provides the session data, such as the user profile ID, to ensure the created/updated entity is tied to the correct user.
- Form Mapping Functions:
mapFormToEntity
: Converts the form data to the structure required by Supabase, ensuring the data matches the database schema.mergeEntityWithForm
: Ensures that when an entity is loaded for updating, the form fields are correctly populated.
- UI Components:
- The UI components like
ProductFormUiComponent
are responsible for rendering the form, which communicates with the state machine to manage user input and trigger actions like saving or updating.
- Routing:
- After a successful operation (e.g., product creation or update), the state machine handles navigation, typically returning to the previous route.
CrudEntityListStateStateMachine
Class
This class encapsulates the logic for handling a list of entities that are loaded all at once, without pagination. It provides methods for:
- Selection: Users can select multiple items for actions like deletion.
- Querying: Filtering items based on a search query.
- Deletion: Handles deletion of selected items or individual items.
Usage Example: Managing a Product List
1. Smart Component for Displaying a Product List
@Component({
selector: 'nitrokit-product-list',
standalone: true,
imports: [
FormsModule,
// Base layout for listing items
ButtonUiComponent,
RouterLink,
RouterOutlet,
tableComponents, // Table components for displaying the product list
BaseLayoutCrudListUiComponent, // Layout for CRUD list management
DeleteModalUiComponent, // Modal for deletion confirmation
InputUiComponent, // Input for search queries
BadgeUiComponent,
],
templateUrl: './product-list.smart-component.html',
styleUrls: ['./product-list.smart-component.scss'],
})
export class ProductListSmartComponent {
// Injecting the facade service for managing the product state
readonly #facadeService = inject(FacadeService);
// expose the items signal to the template
protected readonly items = this.listState.items;
// State machine for managing the list of products
protected readonly listState = new CrudEntityListStateStateMachine<Database, 'products'>(this.#facadeService.productStateMachine);
}
Component Breakdown:
- Product Filtering: The component uses a search input field to filter the displayed products based on a search query. The filtered items are dynamically computed using the
computed()
method. - List State Management: The component uses the
CrudEntityListStateStateMachine
to handle the product list's CRUD operations, such as querying, selecting, and deleting items.
Methods and Signals of CrudEntityListStateStateMachine
1. Item Selection
-
selectItem(v: boolean, id: string)
: Selects or deselects an individual item by its ID. Thev
parameter determines whether the item is selected.Example:
this.listState.selectItem(true, 'product-id-1');
-
allSelectedChange(event: boolean)
: Selects or deselects all items in the list.Example:
this.listState.allSelectedChange(true); // Select all items
2. Batch Deletion
-
onDeleteSelected()
: Marks the currently selected items for deletion.Example:
this.listState.onDeleteSelected();
-
performRemove()
: Deletes the selected items from the list.Example:
this.listState.performRemove();
-
deleteItem(id: string)
: Marks a single item for deletion by its ID.Example:
this.listState.deleteItem('product-id-1');
3. Querying and Searching
-
setQuery(query: string)
: Sets a query string for filtering the items. The query is stored as a signal and can be accessed by the component to filter the displayed items.Example:
this.listState.setQuery('search term');
Signals and Computed Properties
-
items
: A signal representing the list of items loaded by the entity state machine. This list can be filtered based on the query string.Example:
const products = this.listState.items();
-
selectedItemsList
: A computed signal that provides the list of selected item IDs.Example:
const selectedItems = this.listState.selectedItemsList();
-
allSelected
: A computed signal that indicates whether all items are selected.Example:
const allSelected = this.listState.allSelected();
-
numberOfSelectedItems
: A computed signal that returns the count of selected items.Example:
const selectedCount = this.listState.numberOfSelectedItems();
-
deleteModalOpen
: A computed signal that indicates whether the delete confirmation modal should be displayed (when items are marked for deletion).
How the CrudEntityListStateStateMachine
Works in the Component
In the ProductListSmartComponent
, the state machine is responsible for loading and managing the list of products, while the component handles displaying the products and providing user interactions (such as selecting, deleting, and filtering products).
- Loading Products: On component initialization, the state machine fetches the list of products from the data service.
- Filtering: The user can search through the product list using a search input, and the filtered results are dynamically updated.
- Selection: Users can select individual products or select all products for batch operations.
- Deletion: The user can delete selected products, and a delete confirmation modal is displayed before the deletion is performed.
Have questions?
Still have questions? Talk to support.