@libs-ui/components-switch
v0.2.357-4
Published
> Component chuyển đổi trạng thái Bật/Tắt (Toggle Switch) với hỗ trợ two-way binding Signal và xử lý async kèm cơ chế revert.
Readme
@libs-ui/components-switch
Component chuyển đổi trạng thái Bật/Tắt (Toggle Switch) với hỗ trợ two-way binding Signal và xử lý async kèm cơ chế revert.
Giới thiệu
LibsUiComponentsSwitchComponent là một standalone Angular component cung cấp giao diện Switch Button (Toggle). Component hỗ trợ 2 kích thước (default và large), trạng thái disabled, và sử dụng Signal Model cho two-way binding. Khi toggle, component tự động cập nhật state trước khi emit event, đồng thời cung cấp hàm revert() để hoàn tác nếu cần (ví dụ: API call thất bại).
Tính năng
- ✅ Two-way binding với Angular Signal Model (
[(active)]) - ✅ 2 kích thước:
defaultvàlarge - ✅ Trạng thái disabled (vô hiệu hóa tương tác)
- ✅ Event output
(outSwitch)kèm hàmrevert()để hoàn tác state - ✅ Hỗ trợ xử lý async (chờ API response rồi quyết định giữ hay revert)
- ✅ Standalone component với
ChangeDetectionStrategy.OnPush
Khi nào sử dụng
- Khi người dùng cần bật/tắt một tính năng và hành động có hiệu lực ngay lập tức.
- Thay thế cho Checkbox trong các màn hình cài đặt hệ thống, quản lý cấu hình.
- Khi cần xác nhận toggle qua API trước khi chính thức cập nhật state (dùng
revert()nếu API lỗi).
Cài đặt
npm install @libs-ui/components-switchImport
import { LibsUiComponentsSwitchComponent } from '@libs-ui/components-switch';
@Component({
standalone: true,
imports: [LibsUiComponentsSwitchComponent],
// ...
})
export class MyComponent {}Nếu cần dùng interface trong TypeScript:
import { LibsUiComponentsSwitchComponent, ISwitchEvent } from '@libs-ui/components-switch';Ví dụ sử dụng
1. Basic — Two-way binding cơ bản
<!-- template.html -->
<div class="flex items-center gap-3">
<libs_ui-components-switch [(active)]="isActive" />
<span class="text-sm text-gray-700">
Status: {{ isActive() ? 'ON' : 'OFF' }}
</span>
</div>// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsSwitchComponent } from '@libs-ui/components-switch';
@Component({
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [LibsUiComponentsSwitchComponent],
templateUrl: './my.component.html',
})
export class MyComponent {
protected isActive = signal(false);
}2. Sizes — Kích thước Default và Large
<!-- template.html -->
<div class="flex items-end gap-8">
<div class="flex flex-col items-center gap-2">
<span class="text-xs text-gray-500">Default</span>
<libs_ui-components-switch size="default" [(active)]="isActiveDefault" />
</div>
<div class="flex flex-col items-center gap-2">
<span class="text-xs text-gray-500">Large</span>
<libs_ui-components-switch size="large" [(active)]="isActiveLarge" />
</div>
</div>// component.ts
protected isActiveDefault = signal(false);
protected isActiveLarge = signal(true);3. Disabled State — Vô hiệu hóa tương tác
<!-- template.html -->
<div class="flex gap-8">
<!-- Disabled & đang bật -->
<div class="flex flex-col items-center gap-2">
<span class="text-xs text-gray-500">Disabled (ON)</span>
<libs_ui-components-switch [disable]="true" [active]="true" />
</div>
<!-- Disabled & đang tắt -->
<div class="flex flex-col items-center gap-2">
<span class="text-xs text-gray-500">Disabled (OFF)</span>
<libs_ui-components-switch [disable]="true" [active]="false" />
</div>
</div>4. Async Handling — Xử lý API và revert nếu lỗi
<!-- template.html -->
<div class="flex items-center gap-4">
<libs_ui-components-switch
[(active)]="isActiveAsync"
[disable]="isProcessing()"
(outSwitch)="handlerAsyncSwitch($event)" />
@if (isProcessing()) {
<span class="text-sm text-blue-500">Đang xử lý...</span>
}
</div>// component.ts
import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
import { LibsUiComponentsSwitchComponent, ISwitchEvent } from '@libs-ui/components-switch';
@Component({
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [LibsUiComponentsSwitchComponent],
templateUrl: './my.component.html',
})
export class MyComponent {
protected isActiveAsync = signal(false);
protected isProcessing = signal(false);
protected async handlerAsyncSwitch(event: ISwitchEvent): Promise<void> {
event.stopPropagation?.();
this.isProcessing.set(true);
try {
// Gọi API — ví dụ: lưu cài đặt thông báo
await this.settingsService.updateNotification(event.active).toPromise();
// Thành công: state giữ nguyên như đã toggle
} catch {
// Thất bại: revert lại state ban đầu
await event.revert();
} finally {
this.isProcessing.set(false);
}
}
}5. Lắng nghe sự kiện không dùng two-way binding
<!-- template.html -->
<libs_ui-components-switch
[active]="isFeatureEnabled()"
(outSwitch)="handlerFeatureToggle($event)" />// component.ts
protected isFeatureEnabled = signal(false);
protected handlerFeatureToggle(event: ISwitchEvent): void {
event.stopPropagation?.();
console.log('Trạng thái mới:', event.active);
// Tự quản lý state ngoài, không dùng [(active)] two-way binding
this.isFeatureEnabled.set(event.active);
}@Input()
| Input | Type | Default | Mô tả | Ví dụ |
|---|---|---|---|---|
| [(active)] / [active] | boolean (Signal Model) | false | Trạng thái bật/tắt của switch. Hỗ trợ two-way binding với [(active)] hoặc one-way với [active]. | [(active)]="isActive" |
| [size] | 'default' \| 'large' | 'default' | Kích thước hiển thị của switch. | size="large" |
| [disable] | boolean | undefined (falsy) | Vô hiệu hóa switch — người dùng không thể click khi true. Thường dùng khi đang chờ API response. | [disable]="isProcessing()" |
@Output()
| Output | Type | Mô tả | Handler TS | Binding HTML |
|---|---|---|---|---|
| (outSwitch) | ISwitchEvent | Emit sau khi state đã được toggle. Cung cấp active (trạng thái mới) và revert() để hoàn tác về trạng thái trước đó. | protected handlerSwitch(event: ISwitchEvent): void { event.stopPropagation?.(); ... } | (outSwitch)="handlerSwitch($event)" |
Types & Interfaces
import { ISwitchEvent, ISwitch } from '@libs-ui/components-switch';/**
* Event emit khi người dùng click vào switch.
* State đã được toggle trước khi event này được emit.
*/
export interface ISwitchEvent {
/** Trạng thái mới sau khi toggle (true = ON, false = OFF) */
active: boolean;
/** Hàm async để hoàn tác state về trước khi toggle — dùng khi API call thất bại */
revert: () => Promise<void>;
}
/**
* Interface mô tả cấu hình của Switch component.
* Có thể dùng làm type cho object config truyền vào.
*/
export interface ISwitch {
disable?: boolean;
active?: boolean;
action?: (event: ISwitchEvent) => Promise<void>;
}Lưu ý quan trọng
⚠️ State toggle trước khi emit event: Component tự động toggle active state trước khi phát event (outSwitch). Điều này có nghĩa là event.active trong handler đã là giá trị MỚI. Nếu muốn giữ nguyên state (chờ API xác nhận), hãy gọi await event.revert() trong khối catch.
⚠️ Dùng [disable] khi đang xử lý async: Để tránh user click nhiều lần trong khi API đang chạy, truyền [disable]="isProcessing()" vào component. Điều này khóa switch cho đến khi xử lý xong.
⚠️ [(active)] vs [active]: Dùng [(active)] (two-way binding với Signal Model) khi muốn component tự quản lý state. Dùng [active] (one-way) kết hợp với (outSwitch) khi cần kiểm soát state thủ công từ bên ngoài.
⚠️ Không truyền hàm vào template: Tuân thủ convention dự án, tất cả logic xử lý event phải nằm trong handler method của component, không viết inline trên HTML.
Demo
npx nx serve core-uiTruy cập: http://localhost:4500/components/switch
