Nitrokit Nitrokit - Ship faster with AI - No more heavy lifting, build Angular apps at NITROSPEED!

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

  1. 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.
  1. 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.
  1. 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.
  1. 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:

  1. Selection: Users can select multiple items for actions like deletion.
  2. Querying: Filtering items based on a search query.
  3. 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,
    BaseLayoutListViewUiComponent,  // Base layout for listing items
    ButtonUiComponent,
    RouterLink,
    RouterOutlet,
    tableComponents,  // Table components for displaying the product list
    BaseLayoutCrudListComponent,  // 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. The v 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).

  1. Loading Products: On component initialization, the state machine fetches the list of products from the data service.
  2. Filtering: The user can search through the product list using a search input, and the filtered results are dynamically updated.
  3. Selection: Users can select individual products or select all products for batch operations.
  4. 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.