Component State Management
August 18, 2025 ยท View on GitHub
This guide covers reactive state management for UI components using Angular signals.
ComponentState
The ComponentState class provides comprehensive reactive state management for common component operations like dialogs, loading states, and CRUD operations.
Basic Usage
import { Component } from '@angular/core';
import { ComponentState, ManipulationType } from 'ngx-primeng-toolkit';
@Component({
selector: 'app-user-management',
template: `
<div>
<h2>{{ componentState.componentTitleWithManipulationType() }}</h2>
<!-- Loading indicator -->
<div *ngIf="componentState.isAnyAjaxOperationRunning()">
Loading...
</div>
<!-- Action buttons -->
<button (click)="openCreateDialog()"
[disabled]="componentState.isAnyAjaxOperationRunning()">
Create User
</button>
<button (click)="openUpdateDialog()"
[disabled]="!selectedUser || componentState.isAnyAjaxOperationRunning()">
Update User
</button>
<!-- Create/Update Dialog -->
<p-dialog [visible]="componentState.isCreateOrUpdateDialogOpen()"
[header]="getDialogTitle()"
(onHide)="closeDialog()">
<div *ngIf="componentState.isOnCreateState()">
<!-- Create form content -->
</div>
<div *ngIf="componentState.isOnUpdateState()">
<!-- Update form content -->
</div>
</p-dialog>
<!-- Data table with selection -->
<p-table [value]="users"
[(selection)]="selectedUser"
[selectionMode]="componentState.hasMultipleSelection() ? 'multiple' : 'single'"
[checkboxSelection]="componentState.enableCheckBoxSelection()">
<!-- table content -->
</p-table>
</div>
`
})
export class UserManagementComponent {
componentState = new ComponentState();
users: User[] = [];
selectedUser: User | null = null;
ngOnInit() {
this.componentState
.updateComponentTitle('User Management')
.updateMultipleSelectionStatus(false)
.updateCheckBoxSelectionStatus(true);
}
openCreateDialog() {
this.componentState
.updateManipulationType(ManipulationType.CREATE)
.updateCreateOrUpdateDialogStatus(true);
}
openUpdateDialog() {
if (!this.selectedUser) return;
this.componentState
.updateManipulationType(ManipulationType.UPDATE)
.updateCreateOrUpdateDialogStatus(true);
}
closeDialog() {
this.componentState
.updateCreateOrUpdateDialogStatus(false)
.updateManipulationType(ManipulationType.NONE);
}
getDialogTitle(): string {
return this.componentState.isOnCreateState() ? 'Create User' : 'Update User';
}
async saveUser() {
this.componentState.updateAjaxRequestOutgoingStatus(true);
try {
if (this.componentState.isOnCreateState()) {
await this.userService.createUser(this.userForm.value);
} else {
await this.userService.updateUser(this.selectedUser!.id, this.userForm.value);
}
this.closeDialog();
await this.refreshUserList();
} catch (error) {
// Handle error
} finally {
this.componentState.updateAjaxRequestOutgoingStatus(false);
}
}
}
Advanced State Management
@Component({
selector: 'app-advanced-component-state'
})
export class AdvancedComponentStateComponent {
componentState = new ComponentState();
ngOnInit() {
// Configure multiple aspects at once using fluent API
this.componentState
.updateComponentTitle('Product Management')
.updateMultipleSelectionStatus(true)
.updateCheckBoxSelectionStatus(true)
.updateSelectableRowStatus(true);
}
// Handle different manipulation types
handleViewOperation(item: any) {
this.componentState
.updateManipulationType(ManipulationType.VIEW)
.updateCreateOrUpdateDialogStatus(true);
// Load read-only view
}
handleDeleteOperation(item: any) {
this.componentState.updateManipulationType(ManipulationType.DELETE);
// Show confirmation dialog
}
// Handle bulk operations
async handleBulkDelete() {
if (!this.componentState.hasMultipleSelection()) return;
this.componentState.updateAjaxRequestOutgoingStatus(true);
try {
await this.performBulkDelete();
} finally {
this.componentState.updateAjaxRequestOutgoingStatus(false);
}
}
// Handle data loading states
async loadData() {
this.componentState.updateAjaxDataIncomingStatus(true);
try {
const data = await this.dataService.getData();
// Process data
} finally {
this.componentState.updateAjaxDataIncomingStatus(false);
}
}
}
ComponentState Properties
Writable Signals
isAjaxDataIncoming- Incoming data loading stateisAjaxRequestOutgoing- Outgoing request stateenableCheckBoxSelection- Checkbox selection enabled stateisSelectableRowEnabled- Row selection enabled statehasMultipleSelection- Multiple selection mode stateisCreateOrUpdateDialogOpen- Generic dialog open stateisUpdateDialogOpen- Update dialog specific stateisCreateDialogOpen- Create dialog specific statemanipulationType- Current operation type (Create/Update/Delete/View)componentTitle- Component title
Computed Signals
componentTitleWithManipulationType()- Title combined with operation typeisOnUpdateState()- True if in update modeisOnCreateState()- True if in create modeisOnDeleteState()- True if in delete modeisOnViewState()- True if in view modeisAnyAjaxOperationRunning()- True if any Ajax operation is activeisAnyDialogOpen()- True if any dialog is open
ComponentState Methods
All methods return this for fluent API chaining:
Dialog Management
updateCreateOrUpdateDialogStatus(status: boolean)- Set generic dialog stateupdateCreateDialogStatus(status: boolean)- Set create dialog stateupdateUpdateDialogStatus(status: boolean)- Set update dialog state
Ajax State Management
updateAjaxDataIncomingStatus(status: boolean)- Set incoming data loading stateupdateAjaxRequestOutgoingStatus(status: boolean)- Set outgoing request state
Selection Management
updateCheckBoxSelectionStatus(status: boolean)- Enable/disable checkbox selectionupdateSelectableRowStatus(status: boolean)- Enable/disable row selectionupdateMultipleSelectionStatus(status: boolean)- Set multiple selection mode
Operation Management
updateManipulationType(type: ManipulationType)- Set current operation typeupdateComponentTitle(title: string)- Set component title
ManipulationType Enum
export enum ManipulationType {
NONE = 'NONE',
CREATE = 'CREATE',
UPDATE = 'UPDATE',
DELETE = 'DELETE',
VIEW = 'VIEW'
}
Integration with Forms
@Component({
selector: 'app-form-integration'
})
export class FormIntegrationComponent {
componentState = new ComponentState();
userForm: FormGroup;
constructor(private fb: FormBuilder) {
this.userForm = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]]
});
}
openCreateForm() {
this.userForm.reset();
this.componentState
.updateManipulationType(ManipulationType.CREATE)
.updateCreateDialogStatus(true);
}
openUpdateForm(user: User) {
this.userForm.patchValue(user);
this.componentState
.updateManipulationType(ManipulationType.UPDATE)
.updateUpdateDialogStatus(true);
}
async submitForm() {
if (this.userForm.invalid) return;
this.componentState.updateAjaxRequestOutgoingStatus(true);
try {
const formData = this.userForm.value;
if (this.componentState.isOnCreateState()) {
await this.userService.createUser(formData);
} else if (this.componentState.isOnUpdateState()) {
await this.userService.updateUser(this.selectedUser.id, formData);
}
this.closeAllDialogs();
await this.refreshData();
} catch (error) {
this.handleError(error);
} finally {
this.componentState.updateAjaxRequestOutgoingStatus(false);
}
}
closeAllDialogs() {
this.componentState
.updateCreateDialogStatus(false)
.updateUpdateDialogStatus(false)
.updateCreateOrUpdateDialogStatus(false)
.updateManipulationType(ManipulationType.NONE);
}
}
Best Practices
State Organization
// Good: Use computed signals for derived state
get isFormDisabled() {
return this.componentState.isAnyAjaxOperationRunning() ||
this.componentState.manipulationType() === ManipulationType.VIEW;
}
// Good: Use fluent API for multiple updates
this.componentState
.updateComponentTitle('User Management')
.updateMultipleSelectionStatus(true)
.updateCheckBoxSelectionStatus(false);
Error Handling
async performOperation() {
this.componentState.updateAjaxRequestOutgoingStatus(true);
try {
await this.apiCall();
} catch (error) {
// Handle error but don't update loading state here
this.showErrorMessage(error);
} finally {
// Always reset loading state in finally block
this.componentState.updateAjaxRequestOutgoingStatus(false);
}
}
Dialog Management
// Good: Centralized dialog closing
closeAllDialogs() {
this.componentState
.updateCreateOrUpdateDialogStatus(false)
.updateManipulationType(ManipulationType.NONE);
// Reset form if needed
this.form.reset();
}
// Good: Consistent dialog opening pattern
openDialog(type: ManipulationType, data?: any) {
this.componentState
.updateManipulationType(type)
.updateCreateOrUpdateDialogStatus(true);
if (data && type === ManipulationType.UPDATE) {
this.form.patchValue(data);
} else {
this.form.reset();
}
}